<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.3">Jekyll</generator><link href="https://unexist.blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://unexist.blog/" rel="alternate" type="text/html" /><updated>2026-01-27T10:30:34+01:00</updated><id>https://unexist.blog/feed.xml</id><title type="html">unexist.dev</title><subtitle>Blurring lines between tech, software design, architecture and myself.</subtitle><entry><title type="html">Fun with embeddability</title><link href="https://unexist.blog/tech/2026/01/25/embedding-other-languages.html" rel="alternate" type="text/html" title="Fun with embeddability" /><published>2026-01-25T17:55:00+01:00</published><updated>2026-01-25T17:55:00+01:00</updated><id>https://unexist.blog/tech/2026/01/25/embedding-other-languages</id><content type="html" xml:base="https://unexist.blog/tech/2026/01/25/embedding-other-languages.html"><![CDATA[<div class="paragraph">
<p><strong>Embeddability</strong>?
I am quite certain this is in fact an <a href="https://en.wiktionary.org/wiki/embeddability">english word</a>, but you probably won&#8217;t find it
among the other <a href="https://en.wikipedia.org/wiki/List_of_system_quality_attributes">-ilities</a> of qualities, software might want to address.
Definitely among it, there are some boring and pretty self-explanatory ones like
<a href="https://en.wikipedia.org/wiki/Maintainability">maintainability</a>, even some dogmatic ones like
<a href="https://en.wikipedia.org/wiki/Correctness_(computer_science)">correctness</a><sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup>, but fortunately also funnier ones like
<a href="https://en.wikipedia.org/wiki/Extensibility">extensibility</a>.</p>
</div>
<div class="paragraph">
<p>And like to often, how a certain quality is best achieved depends on a plethora of things, but
according to <a href="https://en.wikipedia.org">Wikipedia</a>, one to archive extensibility is to use scripting languages
and everything finally comes together:
<strong>They can be quite embeddable.</strong></p>
</div>
<div class="paragraph">
<p>So in case you have some time to kill, join me on a lengthy journey through 20+ years of personal
<a href="https://en.wikipedia.org/wiki/Free_and_open-source_software">FOSS</a>-history.
We are having a look at different approaches of embeddings and also see why this is always great
idea - plus there are memes.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/fun_with_embeddability/meme-go_wrong.png" alt="Meme Go Wrong">
</div>
<div class="title"><a href="https://knowyourmeme.com/photos/930538-the-fairly-oddparents" class="bare">https://knowyourmeme.com/photos/930538-the-fairly-oddparents</a></div>
</div>
<div class="sect1">
<h2 id="barebone">Barebone</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Unbeknownst to my past self, I made my first experience with this kind of extensibility in 2004,
when I started my long journey with <a href="https://en.wikipedia.org/wiki/Correctness_(computer_science)">Xlib</a>.
During that time I started a project called <a href="https://github.com/unexist/deskbar">deskbar</a> with the lofty goal to print system
information like cpu load, battery usage etc. directly onto the root window of the X session.
There were plenty of alternatives like <a href="https://gkrellm.srcbox.net/">GKrellM</a> readily available, but who in their right
mind prefers pre-built stuff over rolling your own<sup class="footnote">[<a id="_footnoteref_2" class="footnote" href="#_footnotedef_2" title="View footnote.">2</a>]</sup>?</p>
</div>
<div class="paragraph">
<p>The initial idea was just to include everything in one binary, but I quickly discovered the
ergonomics of re-compiling and shipping everything together are annoying and I switched to a simple
plugin system.</p>
</div>
<div class="sect2">
<h3 id="screenshots-first">Screenshots first</h3>
<div class="paragraph">
<p>I would have loved to show some screenshots of deskbar in action here, but unfortunately after
messing with the infamous <a href="https://en.wikipedia.org/wiki/GNU_Autotools">Autotools</a> and trying to compile old <a href="https://en.wikipedia.org/wiki/C_(programming_language)">C</a>-code with a
modern compiler this is as far as I got<sup class="footnote">[<a id="_footnoteref_3" class="footnote" href="#_footnotedef_3" title="View footnote.">3</a>]</sup>:</p>
</div>
<div class="listingblock">
<div class="title">Build #1 attempt of <a href="https://github.com/unexist/deskbar">deskbar</a></div>
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>./configure <span class="o">&amp;&amp;</span> make
deskbar 0.1
<span class="nt">-----------------</span>
Build with ZLIB support.......: <span class="nb">yes
</span>Build with PNG support........: <span class="nb">yes

</span>Plugins:
Common Plugins................: Clock CPU Date
Battery Plugin................: no
XMMS Plugin...................: no <i class="conum" data-value="1"></i><b>(1)</b>
BMP Plugin....................: no <i class="conum" data-value="2"></i><b>(2)</b>
Debug Plugin..................: no

The binary will be installed <span class="k">in</span> /usr/local/bin,
the lib <span class="k">in</span> /usr/local/lib and the plugins
<span class="k">in</span> /usr/local/lib/deskbar.

Try make now, good luck!

make  all-recursive
make[1]: Entering directory <span class="s1">'/home/unexist/build/deskbar-0.1'</span>

<span class="c"># --- %&lt; --- snip --- %&lt; ---</span>

/bin/bash ../libtool  <span class="nt">--tag</span><span class="o">=</span>CC   <span class="nt">--mode</span><span class="o">=</span>compile gcc <span class="nt">-DHAVE_CONFIG_H</span> <span class="nt">-I</span><span class="nb">.</span> <span class="nt">-I</span>..     <span class="nt">-g</span> <span class="nt">-O2</span>  <span class="nt">-I</span>/usr/include <span class="nt">-I</span>/usr/include <span class="nt">-MT</span> htable.lo <span class="nt">-MD</span> <span class="nt">-MP</span> <span class="nt">-MF</span> .deps/htable.Tpo <span class="nt">-c</span> <span class="nt">-o</span> htable.lo htable.c
libtool: compile:  gcc <span class="nt">-DHAVE_CONFIG_H</span> <span class="nt">-I</span><span class="nb">.</span> <span class="nt">-I</span>.. <span class="nt">-g</span> <span class="nt">-O2</span> <span class="nt">-I</span>/usr/include <span class="nt">-I</span>/usr/include <span class="nt">-MT</span> htable.lo <span class="nt">-MD</span> <span class="nt">-MP</span> <span class="nt">-MF</span> .deps/htable.Tpo <span class="nt">-c</span> htable.c  <span class="nt">-fPIC</span> <span class="nt">-DPIC</span> <span class="nt">-o</span> .libs/htable.o
In file included from htable.c:2:
/usr/include/string.h:466:13: error: storage class specified <span class="k">for </span>parameter <span class="s1">'explicit_bzero'</span>
  466 | extern void explicit_bzero <span class="o">(</span>void <span class="k">*</span>__s, size_t __n<span class="o">)</span> __THROW __nonnull <span class="o">((</span>1<span class="o">))</span> <i class="conum" data-value="3"></i><b>(3)</b>
      |             ^~~~~~~~~~~~~~
/usr/include/string.h:471:14: error: make[2]: <span class="k">***</span> <span class="o">[</span>Makefile:457: htable.lo] Error 1
make[2]: Leaving directory <span class="s1">'/home/unexist/build/deskbar-0.1/libdeskbar'</span>
make[1]: <span class="k">***</span> <span class="o">[</span>Makefile:479: all-recursive] Error 1
make[1]: Leaving directory <span class="s1">'/home/unexist/build/deskbar-0.1'</span>
make: <span class="k">***</span> <span class="o">[</span>Makefile:374: all] Error 2storage class specified <span class="k">for </span>parameter <span class="s1">'strsep'</span>
  471 | extern char <span class="k">*</span>strsep <span class="o">(</span>char <span class="k">**</span>__restrict __stringp,
      |              ^~~~~~
/usr/include/string.h:478:14: error: storage class specified <span class="k">for </span>parameter <span class="s1">'strsignal'</span>
  478 | extern char <span class="k">*</span>strsignal <span class="o">(</span>int __sig<span class="o">)</span> __THROW<span class="p">;</span>
      |              ^~~~~~~~~

<span class="c"># --- %&lt; --- snip --- %&lt; ---</span>

make[2]: <span class="k">***</span> <span class="o">[</span>Makefile:457: htable.lo] Error 1
make[2]: Leaving directory <span class="s1">'/home/unexist/build/deskbar-0.1/libdeskbar'</span>
make[1]: <span class="k">***</span> <span class="o">[</span>Makefile:479: all-recursive] Error 1
make[1]: Leaving directory <span class="s1">'/home/unexist/build/deskbar-0.1'</span>
make: <span class="k">***</span> <span class="o">[</span>Makefile:374: all] Error 2</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td><a href="https://en.wikipedia.org/wiki/XMMS">X Multimedia System (XMMS)</a></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>I can only guess what is was supposed to do, since the plugin is just an empty stub that returns <code>NULL</code></td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Yes, oh well&#8230;&#8203;</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Nevertheless, this output clearly proves there has been a plugin system with conditional
compilation, which bases solely on linking magic, and we have to move on.</p>
</div>
<div class="sidebarblock">
<div class="content">
<div class="title">Dang it!</div>
<div class="paragraph">
<p>I dug a bit further and stumbled upon my old project page on <a href="https://sourceforge.net/">SourceForge</a>, which
luckily still provides sftp access to the project page:</p>
</div>
<div class="paragraph">
<p><a href="https://deskbar.sourceforge.net" class="bare">https://deskbar.sourceforge.net</a></p>
</div>
<div class="paragraph">
<p>And with even more luck, although the page is a bit unfinished, the file listing included
screenshots:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/fun_with_embeddability/deskbar-05-05-2005.png" alt="Screenshot of deskbar #1">
</div>
<div class="title">Screenshot of <a href="https://github.com/unexist/deskbar">deskbar-0.7c (1/2)</a></div>
</div>
<div class="paragraph">
<p></p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/fun_with_embeddability/deskbar-shot.png" alt="Screenshot of deskbar #2">
</div>
<div class="title">Screenshot of <a href="https://github.com/unexist/deskbar">deskbar-0.7c (2/2)</a></div>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="runtime-loading">Runtime loading</h3>
<div class="paragraph">
<p>Everything in C is a bit more complicated, so let us ignore the scary memory handling and just
talk about the two interesting calls <a href="https://man7.org/linux/man-pages/man3/dlopen.3.html">dlopen</a> and <a href="https://man7.org/linux/man-pages/man3/dlsym.3.html">dlsym</a>:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/deskbar">deskbar</a>/deskbar/plug.c:97</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="n">DbPlugElement</span> <span class="o">*</span><span class="n">element</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>

<span class="n">element</span> <span class="o">=</span> <span class="p">(</span><span class="n">DbPlugElement</span> <span class="o">*</span><span class="p">)</span> <span class="n">malloc</span> <span class="p">(</span><span class="k">sizeof</span> <span class="p">(</span><span class="n">DbPlugElement</span><span class="p">));</span>

<span class="n">snprintf</span> <span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span> <span class="p">(</span><span class="n">buf</span><span class="p">),</span> <span class="s">"%s/%s.so"</span><span class="p">,</span> <span class="n">PLUGIN_DIR</span><span class="p">,</span> <span class="n">file</span><span class="p">);</span>

<span class="n">element</span><span class="o">-&gt;</span><span class="n">handle</span> <span class="o">=</span> <span class="n">dlopen</span> <span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">RTLD_LAZY</span><span class="p">);</span> <i class="conum" data-value="1"></i><b>(1)</b>

<span class="k">if</span> <span class="p">((</span><span class="n">err</span> <span class="o">=</span> <span class="n">dlerror</span> <span class="p">()))</span> <i class="conum" data-value="2"></i><b>(2)</b>
    <span class="p">{</span>
        <span class="n">db_log_err</span> <span class="p">(</span><span class="s">"Cannot load plugin `%s'</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">file</span><span class="p">);</span>
        <span class="n">db_log_debug</span> <span class="p">(</span><span class="s">"dlopen (): %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">err</span><span class="p">);</span>

        <span class="n">free</span> <span class="p">(</span><span class="n">element</span><span class="p">);</span>

        <span class="k">return</span><span class="p">;</span>
    <span class="p">}</span>

<span class="cm">/* Get entrypoint and call it */</span>
<span class="n">entrypoint</span>      <span class="o">=</span> <span class="n">dlsym</span> <span class="p">(</span><span class="n">element</span><span class="o">-&gt;</span><span class="n">handle</span><span class="p">,</span> <span class="s">"db_plug_init"</span><span class="p">);</span> <i class="conum" data-value="3"></i><b>(3)</b>
<span class="n">element</span><span class="o">-&gt;</span><span class="n">data</span>   <span class="o">=</span> <span class="p">(</span><span class="o">*</span><span class="n">entrypoint</span><span class="p">)</span> <span class="p">();</span> <i class="conum" data-value="4"></i><b>(4)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Load the named shared object from path</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>There is apparently a third call, but rarely mentioned at all</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Find the address of a named entrypoint</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Execute the entrypoint for profit</td>
</tr>
</table>
</div>
<div class="sidebarblock">
<div class="content">
<div class="title">Excursion: Linking in a Nutshell</div>
<div class="paragraph">
<p>Linking is complex topic, but in a nutshell during the linking process all intermediate parts
obj(ect)-files and static libraries) are put together and rolled into a final executable binary or
library:</p>
</div>
<p><object data='/uml/02cdebeee8eef13c7ae84f1191ce306f.svg' type='image/svg+xml'  class='plantuml'></object></p>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Static libraries are directly included in the resulting artifact</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Object files are the compiled form of the source code</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td><a href="https://en.wikipedia.org/wiki/Shared_library">Shared objects</a> can be loaded at runtime</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>The result can either be a shared, library or executable type</td>
</tr>
</table>
</div>
</div>
</div>
<div class="paragraph">
<p>The entrypoint here is quite interesting, since the main application cannot know what is included
in the plugin or even what is exported.
Following the idea of <a href="https://en.wikipedia.org/wiki/Convention_over_configuration">Convention-over-configuration</a>, the defined contract here
expects a symbol named <code>db_plug_init</code> inside a plugin, which is called on load and <strong>must</strong> return
a pointer to an initialized struct of type <code>DBPlug</code>:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/deskbar">deskbar</a>/plugins/battery.c:107</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="k">static</span> <span class="n">DbPlug</span> <span class="n">plugin</span> <span class="o">=</span>
<span class="p">{</span>
    <span class="s">"Battery"</span><span class="p">,</span>       <span class="cm">/* Plugin name */</span>
    <span class="n">battery_create</span><span class="p">,</span>  <span class="cm">/* Plugin create function */</span>
    <span class="n">battery_update</span><span class="p">,</span>  <span class="cm">/* Plugin update function */</span>
    <span class="n">battery_destroy</span><span class="p">,</span> <span class="cm">/* Plugin destroy function */</span>

    <span class="o">&amp;</span><span class="n">data</span><span class="p">,</span>           <span class="cm">/* Plugin data */</span>
    <span class="nb">NULL</span><span class="p">,</span>            <span class="cm">/* Plugin format */</span>

    <span class="mi">3600</span>             <span class="cm">/* Plugin update interval */</span>
<span class="p">};</span>

<span class="n">DbPlug</span> <span class="o">*</span>
<span class="nf">db_plug_init</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">plug</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">plugin</span><span class="p">;</span>

    <span class="k">return</span> <span class="p">(</span><span class="o">&amp;</span><span class="n">plugin</span><span class="p">);</span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Pass the local address back to the main application</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Once loaded the plugin is called in the given interval and can exchange data with the main
application.</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/deskbar">deskbar</a>/plugins/battery.c:58</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="kt">void</span>
<span class="nf">battery_update</span> <span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">int</span> <span class="n">capacity</span>    <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
    <span class="n">percent</span>         <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="kt">char</span> <span class="n">buf</span><span class="p">[</span><span class="mi">100</span><span class="p">],</span> <span class="n">state</span><span class="p">[</span><span class="mi">20</span><span class="p">];</span>

    <span class="cm">/* Get battery info */</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">fd1</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">snprintf</span> <span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="k">sizeof</span> <span class="p">(</span><span class="n">buf</span><span class="p">),</span> <span class="s">"/proc/acpi/battery/BAT%d/state"</span><span class="p">,</span> <span class="n">bat_slot</span><span class="p">);</span> <i class="conum" data-value="1"></i><b>(1)</b>

            <span class="n">fd1</span> <span class="o">=</span> <span class="n">fopen</span> <span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="s">"r"</span><span class="p">);</span>

            <span class="n">memset</span> <span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="k">sizeof</span> <span class="p">(</span><span class="n">buf</span><span class="p">));</span>
        <span class="p">}</span>
    <span class="k">else</span>
        <span class="n">fseek</span> <span class="p">(</span><span class="n">fd1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">SEEK_SET</span><span class="p">);</span>

    <span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Here the battery plugin checks the battery values from the <a href="https://en.wikipedia.org/wiki/ACPI">ACPI</a> interface</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Allowing contribution this way is really easy and powerful, but like so often comes with a catch.
<a href="https://en.wikipedia.org/wiki/Segmentation_fault">Segmentation faults</a>, the bane of software
engineering, don&#8217;t make halt inside plugins like they should, but they wipe the board and kill the
entire application.</p>
</div>
<div class="paragraph">
<p>I think <a href="https://en.wikipedia.org/wiki/Linus_Torvalds">Torvalds</a> nailed it perfectly and I agree this should never happen:</p>
</div>
<div class="quoteblock">
<blockquote>
<span class="line-through">Mauro, SHUT THE FUCK UP!</span><br>
WE DO NOT BREAK USERSPACE!
</blockquote>
<div class="attribution">
&#8212; Linus Torvals<br>
<cite>https://www.shutupmauro.com/</cite>
</div>
</div>
<div class="paragraph">
<p>I am kind of surprised how far I went in trying to keep problems in the plugin at bay.
The original project included memory management<sup class="footnote">[<a id="_footnoteref_4" class="footnote" href="#_footnotedef_4" title="View footnote.">4</a>]</sup> for
plugins and also applied the next two calls I&#8217;d like to demonstrate next.</p>
</div>
</div>
<div class="sect2">
<h3 id="error-handling">Error handling</h3>
<div class="paragraph">
<p>Handling segmentation faults properly is really difficult and the common sense is normally catch
them and exit gracefully when possible.
Still, there are cases when faults <em>can</em> be safely ignored and a plugin interface is a paragon for
this.</p>
</div>
<div class="paragraph">
<p>This can be done with the pair of <a href="https://man7.org/linux/man-pages/man3/setjmp.3.html">setjmp</a> and <a href="https://man7.org/linux/man-pages/man3/setjmp.3.html">longjmp</a>, which behave for most
practical senses like a <a href="https://learn.microsoft.com/en-us/cpp/c-language/goto-and-labeled-statements-c">goto</a> on steroids:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/deskbar">deskbar</a>/deskbar/plug.c:25</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="k">static</span> <span class="kt">int</span><span class="err">¬</span>                                                                                                                                                                                                                                                                                                                                                              <span class="mi">26</span> <span class="nf">save_call</span> <span class="p">(</span><span class="n">DbPlugElement</span> <span class="o">*</span><span class="n">element</span><span class="p">,</span><span class="err">¬</span>
<span class="n">save_call</span> <span class="p">(</span><span class="n">DbPlugElement</span> <span class="o">*</span><span class="n">element</span><span class="p">,</span>
    <span class="n">DbPlugFunc</span> <span class="n">plugfunc</span>
    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">plugfunc</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">setjmp</span> <span class="p">(</span><span class="n">env</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <i class="conum" data-value="1"></i><b>(1)</b>
                <span class="n">plugfunc</span> <span class="p">();</span>
            <span class="k">else</span>
                <span class="p">{</span>
                    <span class="n">db_log_mesg</span> <span class="p">(</span><span class="s">"Ayyyee! Segmentation fault in plugin %s!</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">element</span><span class="o">-&gt;</span><span class="n">data</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">);</span> <i class="conum" data-value="2"></i><b>(2)</b>
                    <span class="n">db_log_debug</span> <span class="p">(</span><span class="s">"Call to %s () failed</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">name</span><span class="p">);</span>
                    <span class="n">db_plug_unload</span> <span class="p">(</span><span class="n">element</span><span class="p">);</span>

                    <span class="k">return</span> <span class="p">(</span><span class="mi">1</span><span class="p">);</span>
                <span class="p">}</span>
        <span class="p">}</span>

    <span class="k">return</span> <span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Save stack and instruction pointer for later use when it is for the first time; otherwise ditch
the plugin</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Well, different times back then..</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>When the application receives the bad <a href="https://man7.org/linux/man-pages/man7/signal.7.html">signal</a> <code>SISEGV</code>, it checks if there are stored stack
and instruction values and rewinds the stack accordingly:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/deskbar">deskbar</a>/deskbar/sig.c:35</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="k">static</span> <span class="kt">void</span>
<span class="nf">sig_handler</span> <span class="p">(</span><span class="kt">int</span> <span class="n">sig</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">switch</span> <span class="p">(</span><span class="n">sig</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">case</span> <span class="n">SIGSEGV</span><span class="p">:</span>
                <span class="n">longjmp</span> <span class="p">(</span><span class="n">env</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> <i class="conum" data-value="1"></i><b>(1)</b>

                <span class="n">db_log_debug</span> <span class="p">(</span><span class="s">"Something went wrong! Segmentation fault!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
                <span class="n">db_sig_destroy</span> <span class="p">();</span>

                <span class="n">abort</span> <span class="p">();</span>
            <span class="k">break</span><span class="p">;</span>

    <span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Check the values and pass control if necessary; otherwise just bail out</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="recap">Recap</h3>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Ease of use</th>
<th class="tableblock halign-left valign-top">Richness of API</th>
<th class="tableblock halign-left valign-top">Language agnostic</th>
<th class="tableblock halign-left valign-top">Error handling</th>
<th class="tableblock halign-left valign-top">Performance</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Low; requires compilation and linking</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">The API is simple, but can be enriched by the host</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">No; requires plugins to be in C<sup class="footnote">[<a id="_footnoteref_5" class="footnote" href="#_footnotedef_5" title="View footnote.">5</a>]</sup></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Arcane; requires stack unwinding</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Runs natively, so pretty fast</p></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="scripting-languages">Scripting languages</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Three years later in 2007 I continued on building upon my Xlib skills and started my long-lasting
project <a href="https://github.com/unexist/subtle">subtle</a>.</p>
</div>
<div class="paragraph">
<p>Over the years there have been many major breaking changes, from the initial design to the state
it currently is in.
Two of the post-related changes were the integration of the scripting language <a href="https://www.lua.org/">Lua</a> and its
later replacement with <a href="https://www.ruby-lang.org/en/">Ruby</a> after a few years in this glorious <a href="https://subtle.de/issues/1">issue #1</a>.</p>
</div>
<div class="sect2">
<h3 id="integrating-lua">Integrating Lua</h3>
<div class="paragraph">
<p>I am not entire sure where I picked Lua up, but I never played <a href="https://worldofwarcraft.blizzard.com/en-us/">WoW</a> so probably from somewhere
else and I can only talk about the state and API from back then.</p>
</div>
<div class="paragraph">
<p>Adding a scripting language solves quite a few problems:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>File loading and parsing can be offloaded to the language core</p>
</li>
<li>
<p>The language itself comes with a basic subset of things you can do with it</p>
</li>
<li>
<p>Bonus: Config handling can also be directly offloaded</p>
</li>
</ul>
</div>
<div class="sect3">
<h4 id="time-for-screenshots">Time for Screenshots</h4>
<div class="paragraph">
<p>My attempt of trying to compile the project and provide an actual screenshot this time ended
quickly as well:</p>
</div>
<div class="listingblock">
<div class="title">Build attempt #1 of <a href="https://github.com/unexist/subtle">subtle-0.7b</a></div>
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>./configure <span class="o">&amp;&amp;</span> make

<span class="c"># --- %&lt; --- snip --- %&lt; ---</span>

subtle 0.7b
<span class="nt">-----------------</span>
Binary....................: /usr/local/bin
Sublets...................: /usr/local/share/subtle
Config....................: /usr/local/etc/subtle

Debugging messages........:

Try make now, good luck!

make  all-recursive
make[1]: Entering directory <span class="s1">'/home/unexist/build/subtle-0.7b'</span>
Making all <span class="k">in </span>src

<span class="c"># --- %&lt; --- snip --- %&lt; ---</span>

<span class="k">if </span>gcc <span class="nt">-DHAVE_CONFIG_H</span> <span class="nt">-I</span><span class="nb">.</span> <span class="nt">-I</span><span class="nb">.</span> <span class="nt">-I</span>.. <span class="nt">-I</span>..   <span class="nt">-g</span> <span class="nt">-O2</span>  <span class="nt">-I</span>/usr/include/lua5.1  <span class="nt">-g</span> <span class="nt">-O2</span>  <span class="nt">-MT</span> subtle-event.o <span class="nt">-MD</span> <span class="nt">-MP</span> <span class="nt">-MF</span> <span class="s2">".deps/subtle-event.Tpo"</span> <span class="nt">-c</span> <span class="nt">-o</span> subtle-event.o <span class="sb">`</span><span class="nb">test</span> <span class="nt">-f</span> <span class="s1">'event.c'</span> <span class="o">||</span> <span class="nb">echo</span> <span class="s1">'./'</span><span class="sb">`</span>event.c<span class="p">;</span> <span class="se">\</span>
<span class="k">then </span><span class="nb">mv</span> <span class="nt">-f</span> <span class="s2">".deps/subtle-event.Tpo"</span> <span class="s2">".deps/subtle-event.Po"</span><span class="p">;</span> <span class="k">else </span><span class="nb">rm</span> <span class="nt">-f</span> <span class="s2">".deps/subtle-event.Tpo"</span><span class="p">;</span> <span class="nb">exit </span>1<span class="p">;</span> <span class="k">fi
</span>event.c: In <span class="k">function</span> ‘subEventLoop’:
event.c:352:57: error: implicit declaration of <span class="k">function</span> ‘subSubletSift’<span class="p">;</span> did you mean ‘subSubletKill’? <span class="o">[</span><span class="nt">-Wimplicit-function-declaration</span><span class="o">]</span>
  352 |                                                         subSubletSift<span class="o">(</span>1<span class="o">)</span><span class="p">;</span>
      |                                                         ^~~~~~~~~~~~~
      |                                                         subSubletKill
make[2]: <span class="k">***</span> <span class="o">[</span>Makefile:310: subtle-event.o] Error 1
make[2]: Leaving directory <span class="s1">'/home/unexist/build/subtle-0.7b/src'</span>
make[1]: <span class="k">***</span> <span class="o">[</span>Makefile:233: all-recursive] Error 1
make[1]: Leaving directory <span class="s1">'/home/unexist/build/subtle-0.7b'</span>
make: <span class="k">***</span> <span class="o">[</span>Makefile:171: all] Error 2</code></pre>
</div>
</div>
<div class="paragraph">
<p>This is kind of embarrassing for an official release and really have to question the quality in
retrospect, but this won&#8217;t stop us now.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/fun_with_embeddability/meme-few_hours_later.png" alt="Meme Few Hours Laters">
</div>
<div class="title"><a href="https://knowyourmeme.com/memes/spongebob-time-cards" class="bare">https://knowyourmeme.com/memes/spongebob-time-cards</a></div>
</div>
<div class="paragraph">
<p>After a dive into the code there were some obviously problems and also blatant oversights and if
you are interested in the shameful truth here is silly patch:</p>
</div>
<div class="paragraph">
<p><a href="https://gist.github.com/unexist/4ee3cb94c91555b1bac01e23f992b9e4" class="bare">https://gist.github.com/unexist/4ee3cb94c91555b1bac01e23f992b9e4</a></p>
</div>
<div class="paragraph">
<p>And without further ado here is finally the screenshot of the scripting part in action, before we
dive into how this is actually done under the hood:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/fun_with_embeddability/subtle-0.7b.png" alt="Screenshot of subtle #1">
</div>
<div class="title">Screenshot of <a href="https://github.com/unexist/subtle">subtle-0.7b</a> (1/2)</div>
</div>
</div>
<div class="sect3">
<h4 id="runtime-loading-2">Runtime loading</h4>
<div class="paragraph">
<p>Starting with the easy part, offloading the config handling was one of the first things I did and
this made a config like this entirely possible:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/subtle">subtle-0.7b</a>/config/config.lua:1</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="lua"><span class="c1">-- Options config</span>
<span class="n">font</span> <span class="o">=</span> <span class="p">{</span>
    <span class="n">face</span>    <span class="o">=</span> <span class="s2">"lucidatypewriter"</span><span class="p">,</span>  <span class="c1">-- Font face for the text</span>
    <span class="n">style</span>   <span class="o">=</span> <span class="s2">"medium"</span><span class="p">,</span>            <span class="c1">-- Font style (medium|bold|italic)</span>
    <span class="n">size</span>    <span class="o">=</span> <span class="mi">12</span>                   <span class="c1">-- Font size</span>
<span class="p">}</span>

<span class="c1">-- Color config</span>
<span class="n">colors</span> <span class="o">=</span> <span class="p">{</span>
    <span class="n">font</span>       <span class="o">=</span> <span class="s2">"#ffffff"</span><span class="p">,</span>        <span class="c1">-- Color of the font</span>
    <span class="n">border</span>     <span class="o">=</span> <span class="s2">"#ffffff"</span><span class="p">,</span>        <span class="c1">-- Color of the border/tiles</span>
    <span class="n">normal</span>     <span class="o">=</span> <span class="s2">"#CFDCE6"</span><span class="p">,</span>        <span class="c1">-- Color of the inactive windows</span>
    <span class="n">focus</span>      <span class="o">=</span> <span class="s2">"#6096BF"</span><span class="p">,</span>        <span class="c1">-- Color of the focussed window</span>
    <span class="n">shade</span>      <span class="o">=</span> <span class="s2">"#bac5ce"</span><span class="p">,</span>        <span class="c1">-- Color of shaded windows</span>
    <span class="n">background</span> <span class="o">=</span> <span class="s2">"#596F80"</span>         <span class="c1">-- Color of the root background</span>
<span class="p">}</span>

<span class="c1">-- --- %&lt; --- snip --- %&lt; ---</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Essentially the C API of Lua is a stack machine and the interaction with is through pushing and
popping values onto and from the
stack.<sup class="footnote">[<a id="_footnoteref_6" class="footnote" href="#_footnotedef_6" title="View footnote.">6</a>]</sup></p>
</div>
<div class="paragraph">
<p>I&#8217;ve removed a bit of the fluff and checks upfront, so we can have a quick glance at the config
loading and jump further into nitty-gritty details:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/subtle">subtle-0.7b</a>/src/lua.c:150</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>

<span class="n">subLogDebug</span><span class="p">(</span><span class="s">"Reading `%s'</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">buf</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="n">luaL_loadfile</span><span class="p">(</span><span class="n">configstate</span><span class="p">,</span> <span class="n">buf</span><span class="p">)</span> <span class="o">||</span> <span class="n">lua_pcall</span><span class="p">(</span><span class="n">configstate</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span> <i class="conum" data-value="1"></i><b>(1)</b>
    <span class="p">{</span>
        <span class="n">subLogDebug</span><span class="p">(</span><span class="s">"%s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">lua_tostring</span><span class="p">(</span><span class="n">configstate</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">));</span>
        <span class="n">lua_close</span><span class="p">(</span><span class="n">configstate</span><span class="p">);</span>
        <span class="n">subLogError</span><span class="p">(</span><span class="s">"Can't load config file `%s'.</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">buf</span><span class="p">);</span>
    <span class="p">}</span>

<span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>

<span class="cm">/* Parse and load the font */</span><span class="err">¬</span>
<span class="n">face</span>  <span class="o">=</span> <span class="n">GetString</span><span class="p">(</span><span class="n">configstate</span><span class="p">,</span> <span class="s">"font"</span><span class="p">,</span> <span class="s">"face"</span><span class="p">,</span> <span class="s">"fixed"</span><span class="p">);</span> <i class="conum" data-value="2"></i><b>(2)</b>
<span class="n">style</span> <span class="o">=</span> <span class="n">GetString</span><span class="p">(</span><span class="n">configstate</span><span class="p">,</span> <span class="s">"font"</span><span class="p">,</span> <span class="s">"style"</span><span class="p">,</span> <span class="s">"medium"</span><span class="p">);</span>
<span class="n">size</span>  <span class="o">=</span> <span class="n">GetNum</span><span class="p">(</span><span class="n">configstate</span><span class="p">,</span> <span class="s">"font"</span><span class="p">,</span> <span class="s">"size"</span><span class="p">,</span> <span class="mi">12</span><span class="p">);</span>

<span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Internal calls to load the config file and just execute it in a safe way <a href="https://www.lua.org/pil/25.2.html">pcall</a></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Once everything is stored inside <code>configstate</code> we fetch required values</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/subtle">subtle-0.7b</a>/src/lua.c:47+72</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="cp">#define GET_GLOBAL(configstate) do { \ <i class="conum" data-value="1"></i><b>(1)</b>
</span>    <span class="n">lua_getglobal</span><span class="p">(</span><span class="n">configstate</span><span class="p">,</span> <span class="n">table</span><span class="p">);</span> <span class="err">\</span> <i class="conum" data-value="2"></i><b>(2)</b>
    <span class="k">if</span><span class="p">(</span><span class="n">lua_istable</span><span class="p">(</span><span class="n">configstate</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">))</span> \
        <span class="p">{</span> \
            <span class="n">lua_pushstring</span><span class="p">(</span><span class="n">configstate</span><span class="p">,</span> <span class="n">field</span><span class="p">);</span> <span class="err">\</span> <i class="conum" data-value="3"></i><b>(3)</b>
            <span class="n">lua_gettable</span><span class="p">(</span><span class="n">configstate</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="p">);</span> \
        <span class="p">}</span> \
<span class="err">}</span> <span class="k">while</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

<span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>

<span class="k">static</span> <span class="kt">char</span> <span class="o">*</span>
<span class="nf">GetString</span><span class="p">(</span><span class="n">lua_State</span> <span class="o">*</span><span class="n">configstate</span><span class="p">,</span>
    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">table</span><span class="p">,</span>
    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">field</span><span class="p">,</span>
    <span class="kt">char</span> <span class="o">*</span><span class="n">fallback</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">GET_GLOBAL</span><span class="p">(</span><span class="n">configstate</span><span class="p">);</span>
    <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="n">lua_isstring</span><span class="p">(</span><span class="n">configstate</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">))</span> <i class="conum" data-value="4"></i><b>(4)</b>
        <span class="p">{</span>
            <span class="n">subLogDebug</span><span class="p">(</span><span class="s">"Expected string, got `%s' for `%s'.</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">lua_typename</span><span class="p">(</span><span class="n">configstate</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">),</span> <span class="n">field</span><span class="p">);</span>
            <span class="k">return</span><span class="p">(</span><span class="n">fallback</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="k">return</span><span class="p">((</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">lua_tostring</span><span class="p">(</span><span class="n">configstate</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">));</span> <i class="conum" data-value="5"></i><b>(5)</b>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Blocks in C macros require this fancy hack; probably best to skip over it</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>We check and fetch a table<sup class="footnote">[<a id="_footnoteref_7" class="footnote" href="#_footnotedef_7" title="View footnote.">7</a>]</sup></td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Push the string onto the current stack</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Pull the value with index -2 from the stack</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>And convert it to our desired format</td>
</tr>
</table>
</div>
<div class="sidebarblock">
<div class="content">
<div class="title">Excursion: Playing with the stack</div>
<div class="paragraph">
<p>If you haven&#8217;t played with stack machines before it might be a bit difficult to follow what it is
done, so here is a small break down how the API works:</p>
</div>
<p><object data='/uml/6fee401e7282bee1ef1b1a2b540df9c8.svg' type='image/svg+xml'  class='plantuml'></object></p>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Call <a href="https://www.lua.org/manual/2.4/node16.html">lua_getglobal</a> to put the table <code>font</code> onto the stack at position
<code>-1</code> <sup class="footnote">[<a id="_footnoteref_8" class="footnote" href="#_footnotedef_8" title="View footnote.">8</a>]</sup></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Call <a href="https://www.lua.org/pil/24.2.1.html">lua_pushstring</a> to put the string <code>face</code> of the desired row name on the
stack at position <code>-1</code></td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Call <a href="https://www.lua.org/pil/25.1.html">lua_gettable</a> to consume both values and fetch the row by given name from
the table and put the result at stack position <code>-1</code></td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Call <a href="https://www.lua.org/pil/24.2.2.html">lua_tostring</a> to convert on the stack at position <code>-1</code> to string if possible</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect3">
<h4 id="error-handling-2">Error handling</h4>
<div class="paragraph">
<p>Loading of plugins at runtime is basically the same as loading the config upfront, so let us just
move on to error handling, which is slightly more interesting.
It is probably no surprise, but the API is quite rudimentary and the handling of the stack and
calls in case of an actual error is up to person to embed the engine.</p>
</div>
<div class="paragraph">
<p>Before we can see how this is done, let us quickly check how our battery plugin evolved from the
arcane version in C to the Lua glory.
First of all, plugins have been rebranded to <a href="https://github.com/unexist/sublets">sublets</a><sup class="footnote">[<a id="_footnoteref_9" class="footnote" href="#_footnotedef_9" title="View footnote.">9</a>]</sup>
and it (at least to me) became a bit more readable:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/subtle">subtle-0.7b</a>/sublets/battery.lua:30</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="lua"><span class="c1">-- Get remaining battery in percent</span>
<span class="k">function</span> <span class="nf">battery</span><span class="p">:</span><span class="n">meter</span><span class="p">()</span> <i class="conum" data-value="1"></i><b>(1)</b>
    <span class="kd">local</span> <span class="n">f</span> <span class="o">=</span> <span class="nb">io.open</span><span class="p">(</span><span class="s2">"/proc/acpi/battery/BAT"</span> <span class="o">..</span> <span class="n">battery</span><span class="p">.</span><span class="n">slot</span> <span class="o">..</span> <span class="s2">"/state"</span><span class="p">,</span> <span class="s2">"r"</span><span class="p">)</span>
    <span class="kd">local</span> <span class="n">info</span> <span class="o">=</span> <span class="n">f</span><span class="p">:</span><span class="n">read</span><span class="p">(</span><span class="s2">"*a"</span><span class="p">)</span>
    <span class="n">f</span><span class="p">:</span><span class="n">close</span><span class="p">()</span>

    <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">battery</span><span class="p">.</span><span class="n">remaining</span> <span class="o">=</span> <span class="nb">string.find</span><span class="p">(</span><span class="n">info</span><span class="p">,</span> <span class="s2">"remaining capacity:%s*(%d+).*"</span><span class="p">)</span>
    <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">battery</span><span class="p">.</span><span class="n">rate</span>      <span class="o">=</span> <span class="nb">string.find</span><span class="p">(</span><span class="n">info</span><span class="p">,</span> <span class="s2">"present rate:%s*(%d+).*"</span><span class="p">)</span>
    <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">battery</span><span class="p">.</span><span class="n">state</span>     <span class="o">=</span> <span class="nb">string.find</span><span class="p">(</span><span class="n">info</span><span class="p">,</span> <span class="s2">"charging state:%s*(%a+).*"</span><span class="p">)</span>

    <span class="k">return</span><span class="p">(</span><span class="nb">math.floor</span><span class="p">(</span><span class="n">battery</span><span class="p">.</span><span class="n">remaining</span> <span class="o">*</span> <span class="mi">100</span> <span class="o">/</span> <span class="n">battery</span><span class="p">.</span><span class="n">capacity</span><span class="p">))</span>
<span class="k">end</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The <code>:</code> here is used as a kind of namespace separator and should be read as a global table
called <code>battery</code> with the entry <code>meter</code>.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Once the sublet is loaded and initialized we can just call it analogue to our <code>save_call</code> from
before:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/subtle">subtle-0.7b</a>/src/lua.c:345</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="kt">void</span>
<span class="nf">subLuaCall</span><span class="p">(</span><span class="n">SubSublet</span> <span class="o">*</span><span class="n">s</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">if</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">lua_settop</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <i class="conum" data-value="1"></i><b>(1)</b>
            <span class="n">lua_rawgeti</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="n">LUA_REGISTRYINDEX</span><span class="p">,</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">ref</span><span class="p">);</span>
            <span class="k">if</span><span class="p">(</span><span class="n">lua_pcall</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span> <i class="conum" data-value="2"></i><b>(2)</b>
                <span class="p">{</span>
                    <span class="k">if</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">&amp;</span> <span class="n">SUB_SUBLET_FAIL_THIRD</span><span class="p">)</span> <i class="conum" data-value="3"></i><b>(3)</b>
                        <span class="p">{</span>
                            <span class="n">subLogWarn</span><span class="p">(</span><span class="s">"Unloaded sublet (#%d) after 3 failed attempts</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">ref</span><span class="p">);</span>
                            <span class="n">subSubletDelete</span><span class="p">(</span><span class="n">s</span><span class="p">);</span>
                            <span class="k">return</span><span class="p">;</span><span class="err">¬</span>
                        <span class="p">}</span>
                    <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">&amp;</span> <span class="n">SUB_SUBLET_FAIL_SECOND</span><span class="p">)</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">|=</span> <span class="n">SUB_SUBLET_FAIL_THIRD</span><span class="p">;</span>
                    <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">&amp;</span> <span class="n">SUB_SUBLET_FAIL_FIRST</span><span class="p">)</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">|=</span> <span class="n">SUB_SUBLET_FAIL_SECOND</span><span class="p">;</span>

                    <span class="n">subLogWarn</span><span class="p">(</span><span class="s">"Failed attempt #%d to call sublet (#%d).</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span>
                        <span class="n">s</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">&amp;</span> <span class="n">SUB_SUBLET_FAIL_SECOND</span><span class="p">)</span> <span class="o">?</span> <span class="mi">2</span> <span class="o">:</span> <span class="mi">1</span><span class="p">,</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">ref</span><span class="p">);</span>
                <span class="p">}</span>

            <span class="k">switch</span><span class="p">(</span><span class="n">lua_type</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">))</span> <i class="conum" data-value="4"></i><b>(4)</b>
                <span class="p">{</span>
                    <span class="k">case</span> <span class="n">LUA_TNIL</span><span class="p">:</span> <span class="n">subLogWarn</span><span class="p">(</span><span class="s">"Sublet (#%d) does not return any usuable value</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">ref</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span>
                    <span class="k">case</span> <span class="n">LUA_TNUMBER</span><span class="p">:</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">number</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">lua_tonumber</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span> <span class="k">break</span><span class="p">;</span>
                    <span class="k">case</span> <span class="n">LUA_TSTRING</span><span class="p">:</span>
                        <span class="k">if</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">string</span><span class="p">)</span> <span class="n">free</span><span class="p">(</span><span class="n">s</span><span class="o">-&gt;</span><span class="n">string</span><span class="p">);</span>
                        <span class="n">s</span><span class="o">-&gt;</span><span class="n">string</span> <span class="o">=</span> <span class="n">strdup</span><span class="p">((</span><span class="kt">char</span> <span class="o">*</span><span class="p">)</span><span class="n">lua_tostring</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">));</span>
                        <span class="k">break</span><span class="p">;</span>
                    <span class="nl">default:</span>
                        <span class="n">subLogDebug</span><span class="p">(</span><span class="s">"Sublet (#%d) returned unkown type %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">s</span><span class="o">-&gt;</span><span class="n">ref</span><span class="p">,</span> <span class="n">lua_typename</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">));</span>
                        <span class="n">lua_pop</span><span class="p">(</span><span class="n">state</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">);</span>
                    <span class="p">}</span>
                <span class="p">}</span>
        <span class="p">}</span>
<span class="err">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>A bit stack setup and retrieval via upfront</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Here we call <a href="https://www.lua.org/pil/25.2.html">lua_pcall</a>, which abstracts and hides the nasty <code>setjmp</code> and <code>longjmp</code>
handling from us</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Looks like I discovered bitflags there and utilized it for error handling</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Type handling for a more generic interface</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="integrating-ruby">Integrating Ruby</h3>
<div class="paragraph">
<p>Moving fast-forward with subtle, I&#8217;ve replaced Lua with <a href="https://www.ruby-lang.org/en/">Ruby</a> after a
while and this is an entirely different way of integration, but let us just stick to our recipe
here and do one mistake after another.</p>
</div>
<div class="sect3">
<h4 id="time-for-screenshots-2">Time for Screenshots</h4>
<div class="paragraph">
<p>This time we can keep it short and simple, since I am using it on a daily on several devices and
can easily provide screenshots without messing with outdated and broken
builds<sup class="footnote">[<a id="_footnoteref_10" class="footnote" href="#_footnotedef_10" title="View footnote.">10</a>]</sup>.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/fun_with_embeddability/subtle-0.12.6606.png" alt="screenshot of subtle #2">
</div>
<div class="title">Screenshot of <a href="https://github.com/unexist/subtle">subtle-0.12.6606</a> (2/2)</div>
</div>
</div>
<div class="sect3">
<h4 id="runtime-loading-3">Runtime loading</h4>
<div class="paragraph">
<p>So when we finally start subtle everything comes together, and we see known pieces from other
projects before, which is more or the less entirely the same.</p>
</div>
<div class="paragraph">
<p>Just feel free to skip the next few listings and join us later and for the ones remaining..</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/fun_with_embeddability/meme-wall_of_text.png" alt="Meme Wall of Text">
</div>
<div class="title"><a href="https://knowyourmeme.com/memes/wall-of-text" class="bare">https://knowyourmeme.com/memes/wall-of-text</a></div>
</div>
<div class="paragraph">
<p>Just kidding, here is the promised triplet of loading info, config and the battery thingy:</p>
</div>
<div class="listingblock">
<div class="title">Running of <a href="https://github.com/unexist/subtle">subtle-0.12.6606</a></div>
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>subtle <span class="nt">-d</span> :2 <span class="nt">-c</span> subtle.rb <span class="nt">-s</span> sublets
subtle 0.12.6606 - Copyright <span class="o">(</span>c<span class="o">)</span> 2005-present Christoph Kappel
Released under the GNU General Public License
Compiled <span class="k">for </span>X11R0 and Ruby 2.7.8
Display <span class="o">(</span>:2<span class="o">)</span> is 640x480
Running on 1 screen<span class="o">(</span>s<span class="o">)</span>
ruby: warning: already initialized constant TMP_RUBY_PREFIX
Reading file <span class="sb">`</span>subtle.rb<span class="s1">'
Reading file `sublets/battery.rb'</span>
Loaded sublet <span class="o">(</span>battery<span class="o">)</span>
Reading file <span class="sb">`</span>sublets/fuzzytime.rb<span class="s1">'
Loaded sublet (fuzzytime)</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>The config looks a bit different, mainly because we are now using a custom <a href="https://en.wikipedia.org/wiki/Domain-specific_language">DSL</a>, but we are
going to cover this part in detail shortly, promised.</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/subtle">subtle-0.12.6606</a>/data/subtle.rb:94</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="ruby"><span class="c1"># Style for all style elements</span>
<span class="n">style</span> <span class="ss">:all</span> <span class="k">do</span> <i class="conum" data-value="1"></i><b>(1)</b>
    <span class="n">foreground</span>  <span class="s2">"#757575"</span>
    <span class="n">background</span>  <span class="s2">"#202020"</span>
    <span class="n">icon</span>        <span class="s2">"#757575"</span>
    <span class="n">padding</span>     <span class="mi">0</span><span class="p">,</span> <span class="mi">3</span>
    <span class="n">font</span>        <span class="s2">"-*-*-*-*-*-*-14-*-*-*-*-*-*-*"</span>
    <span class="c1">#font        "xft:sans-8"</span>
<span class="k">end</span>

<span class="c1"># Style for the all views</span>
<span class="n">style</span> <span class="ss">:views</span> <span class="k">do</span> <i class="conum" data-value="2"></i><b>(2)</b>
    <span class="c1"># Style for the active views</span>
    <span class="n">style</span> <span class="ss">:focus</span> <span class="k">do</span>
        <span class="n">foreground</span>  <span class="s2">"#fecf35"</span>
    <span class="k">end</span>

    <span class="c1"># --- %&lt; --- snip --- %&lt; ---</span>
<span class="k">end</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Ruby is famous for <a href="https://en.wikipedia.org/wiki/Metaprogramming">metaprogramming</a> and we obviously make have use of it here</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td><a href="https://subtle.de/projects/subtle/wiki/Styles">Styles</a> are a CSS-like way of configuring colors in subtle - batteries and
inheritance included</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>And lastly, a quick glimpse into the battery sublet, which naturally also makes use of the
mentioned DSL:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/sublets">battery-0.9</a>/battery.rb:64</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="ruby"><span class="n">on</span> <span class="ss">:run</span> <span class="k">do</span> <span class="o">|</span><span class="n">s</span><span class="o">|</span>
    <span class="k">begin</span> <i class="conum" data-value="1"></i><b>(1)</b>
        <span class="n">now</span>     <span class="o">=</span> <span class="no">IO</span><span class="p">.</span><span class="nf">readlines</span><span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="nf">now</span><span class="p">).</span><span class="nf">first</span><span class="p">.</span><span class="nf">to_i</span>
        <span class="n">state</span>   <span class="o">=</span> <span class="no">IO</span><span class="p">.</span><span class="nf">readlines</span><span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="nf">status</span><span class="p">).</span><span class="nf">first</span><span class="p">.</span><span class="nf">chop</span>
        <span class="n">percent</span> <span class="o">=</span> <span class="p">(</span><span class="n">now</span> <span class="o">*</span> <span class="mi">100</span> <span class="o">/</span> <span class="n">s</span><span class="p">.</span><span class="nf">full</span><span class="p">).</span><span class="nf">to_i</span>

        <span class="c1"># --- %&lt; --- snip --- %&lt; ---</span>

        <span class="c1"># Select icon for state</span>
        <span class="n">icon</span> <span class="o">=</span> <span class="k">case</span> <span class="n">state</span> <i class="conum" data-value="2"></i><b>(2)</b>
            <span class="k">when</span> <span class="s2">"Charging"</span>  <span class="k">then</span> <span class="ss">:ac</span>
            <span class="k">when</span> <span class="s2">"Discharging"</span>
                <span class="k">case</span> <span class="n">percent</span>
                    <span class="k">when</span> <span class="mi">67</span><span class="o">..</span><span class="mi">100</span> <span class="k">then</span> <span class="ss">:full</span>
                    <span class="k">when</span> <span class="mi">34</span><span class="o">..</span><span class="mi">66</span>  <span class="k">then</span> <span class="ss">:low</span>
                    <span class="k">when</span> <span class="mi">0</span><span class="o">..</span><span class="mi">33</span>   <span class="k">then</span> <span class="ss">:empty</span>
                <span class="k">end</span>
            <span class="k">when</span> <span class="s2">"Full"</span>          <span class="k">then</span> <span class="ss">:ac</span>
            <span class="k">else</span>                      <span class="ss">:unknown</span>
        <span class="k">end</span>

        <span class="n">s</span><span class="p">.</span><span class="nf">data</span> <span class="o">=</span> <span class="s2">"%s%s%s%d%%"</span> <span class="o">%</span> <span class="p">[</span>
            <span class="n">s</span><span class="p">.</span><span class="nf">color_icon</span> <span class="p">?</span> <span class="n">s</span><span class="p">.</span><span class="nf">color</span> <span class="p">:</span> <span class="n">s</span><span class="p">.</span><span class="nf">color_def</span><span class="p">,</span> <span class="n">s</span><span class="p">.</span><span class="nf">icons</span><span class="p">[</span><span class="n">icon</span><span class="p">],</span>
            <span class="n">s</span><span class="p">.</span><span class="nf">color_text</span> <span class="p">?</span> <span class="n">s</span><span class="p">.</span><span class="nf">color</span> <span class="p">:</span> <span class="n">s</span><span class="p">.</span><span class="nf">color_def</span><span class="p">,</span> <span class="n">percent</span>
            <span class="p">]</span>
        <span class="k">rescue</span> <span class="o">=&gt;</span> <span class="n">err</span> <span class="c1"># Sanitize to prevent unloading</span>
            <span class="n">s</span><span class="p">.</span><span class="nf">data</span> <span class="o">=</span> <span class="s2">"subtle"</span>
        <span class="nb">p</span> <span class="n">err</span>
    <span class="k">end</span>
<span class="k">end</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Ruby comes with exception handling and this eases the whole scripting part greatly</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Aww, this kind of reminds of Rust &lt;3</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>So when we talk about metaprogramming, what exactly is different here?
If you have a closer look at the previous examples, we mostly defined data structures and
methods there, which were later collected during load and/or actually called by the host
application.
In other words our scripts defined an API according to the rules of the host application, which
then runs it.
With metaprogramming now, we turn this around and define methods and provide an API for our
scripts to let them call it.</p>
</div>
<div class="paragraph">
<p>The Ruby integration in subtle is quite vast, and there are many cool things I&#8217;d like to show, but
time is precious, as is our attention span and sobriety is in order.
So we have to cut a few corners here and there and follow loads of <span class="line-through">indirection</span>
abstraction, but I think we better stay with the styles excerpt from above.</p>
</div>
<div class="paragraph">
<p>Loading styles from the config consists of following basic building blocks:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/subtle">subtle-0.12.6606</a>/src/subtle/ruby.c:3161</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="kt">void</span> <span class="nf">subRubyInit</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">VALUE</span> <span class="n">config</span> <span class="o">=</span> <span class="n">Qnil</span><span class="p">,</span> <span class="n">options</span> <span class="o">=</span> <span class="n">Qnil</span><span class="p">,</span> <span class="n">sublet</span> <span class="o">=</span> <span class="n">Qnil</span><span class="p">;</span>

    <span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>

    <span class="n">config</span> <span class="o">=</span> <span class="n">rb_define_class_under</span><span class="p">(</span><span class="n">mod</span><span class="p">,</span> <span class="s">"Config"</span><span class="p">,</span> <span class="n">rb_cObject</span><span class="p">);</span> <i class="conum" data-value="1"></i><b>(1)</b>

    <span class="cm">/* Class methods */</span><span class="err">¬</span>
    <span class="n">rb_define_method</span><span class="p">(</span><span class="n">config</span><span class="p">,</span> <span class="s">"style"</span><span class="p">,</span> <span class="n">RubyConfigStyle</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> <i class="conum" data-value="2"></i><b>(2)</b>

    <span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Define a holding class for our method definition</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Define the actual method <code>style</code> and bind it to <code>RubyConfigStyle</code></td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/subtle">subtle-0.12.6606</a>/src/subtle/ruby.c:3239</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="kt">void</span> <span class="nf">subRubyLoadConfig</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">VALUE</span> <span class="n">klass</span> <span class="o">=</span> <span class="n">Qnil</span><span class="p">;</span>

    <span class="cm">/* Load supplied config or default */</span>
    <span class="n">klass</span> <span class="o">=</span> <span class="n">rb_const_get</span><span class="p">(</span><span class="n">mod</span><span class="p">,</span> <span class="n">rb_intern</span><span class="p">(</span><span class="s">"Config"</span><span class="p">));</span> <i class="conum" data-value="1"></i><b>(1)</b>
    <span class="n">config_instance</span> <span class="o">=</span> <span class="n">rb_funcall</span><span class="p">(</span><span class="n">klass</span><span class="p">,</span> <span class="n">rb_intern</span><span class="p">(</span><span class="s">"new"</span><span class="p">),</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
    <span class="n">rb_gc_register_address</span><span class="p">(</span><span class="o">&amp;</span><span class="n">config_instance</span><span class="p">);</span> <i class="conum" data-value="2"></i><b>(2)</b>

    <span class="k">if</span> <span class="p">(</span><span class="n">Qfalse</span> <span class="o">==</span> <span class="n">RubyConfigLoadConfig</span><span class="p">(</span><span class="n">config_instance</span><span class="p">,</span><span class="err">¬</span>
        <span class="n">rb_str_new2</span><span class="p">(</span><span class="n">subtle</span><span class="o">-&gt;</span><span class="n">paths</span><span class="p">.</span><span class="n">config</span> <span class="o">?</span> <span class="n">subtle</span><span class="o">-&gt;</span><span class="n">paths</span><span class="p">.</span><span class="n">config</span> <span class="o">:</span> <span class="n">PKG_CONFIG</span><span class="p">)))</span> <span class="p">{</span> <i class="conum" data-value="3"></i><b>(3)</b>
        <span class="n">subSubtleFinish</span><span class="p">();</span>

        <span class="n">exit</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span><span class="err">¬</span>
    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">subtle</span><span class="o">-&gt;</span><span class="n">flags</span> <span class="o">&amp;</span> <span class="n">SUB_SUBTLE_CHECK</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">printf</span><span class="p">(</span><span class="s">"Syntax OK</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Call back our config class and create a new instance</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Take care, that the internal garbage collector doesn&#8217;t get rid of it</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Wrap it again and continue in the next snippet</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/subtle">subtle-0.12.6606</a>/src/subtle/ruby.c:1688</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="k">static</span> <span class="n">VALUE</span> <span class="nf">RubyConfigLoadConfig</span><span class="p">(</span><span class="n">VALUE</span> <span class="n">self</span><span class="p">,</span> <span class="n">VALUE</span> <span class="n">file</span><span class="p">)</span> <span class="p">{</span>
    <span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"Reading file `%s'</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">buf</span><span class="p">);</span>

    <span class="cm">/* Carefully load and eval file */</span>
    <span class="n">rargs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">rb_str_new2</span><span class="p">(</span><span class="n">buf</span><span class="p">);</span>
    <span class="n">rargs</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">self</span><span class="p">;</span>

    <span class="n">rb_protect</span><span class="p">(</span><span class="n">RubyWrapEvalFile</span><span class="p">,</span> <span class="p">(</span><span class="n">VALUE</span><span class="p">)</span> <span class="o">&amp;</span><span class="n">rargs</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">state</span><span class="p">);</span> <i class="conum" data-value="1"></i><b>(1)</b>
    <span class="k">if</span> <span class="p">(</span><span class="n">state</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">subSubtleLogWarn</span><span class="p">(</span><span class="s">"Cannot load file `%s'</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">buf</span><span class="p">);</span>
        <span class="n">RubyBacktrace</span><span class="p">();</span>

        <span class="k">return</span> <span class="n">Qfalse</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">Qtrue</span><span class="p">;</span>
<span class="p">}</span> <span class="cm">/* }}} */</span><span class="err">¬</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Ruby uses its own version of <code>setjmp</code> and <code>longjmp</code>, so wrap everything up and pass it over</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/subtle">subtle-0.12.6606</a>/src/subtle/ruby.c:1442</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="cm">/* RubyWrapEvalFile */</span>
<span class="k">static</span> <span class="n">VALUE</span> <span class="nf">RubyWrapEvalFile</span><span class="p">(</span><span class="n">VALUE</span> <span class="n">data</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">VALUE</span> <span class="o">*</span><span class="n">rargs</span> <span class="o">=</span> <span class="p">(</span><span class="n">VALUE</span> <span class="o">*</span><span class="p">)</span> <span class="n">data</span><span class="p">,</span> <span class="n">rargs2</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="n">Qnil</span><span class="p">};</span>

    <span class="cm">/* Wrap data */</span>
    <span class="n">rargs2</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">rb_funcall</span><span class="p">(</span><span class="n">rb_cFile</span><span class="p">,</span> <span class="n">rb_intern</span><span class="p">(</span><span class="s">"read"</span><span class="p">),</span> <span class="mi">1</span><span class="p">,</span> <span class="n">rargs</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span> <i class="conum" data-value="1"></i><b>(1)</b>
    <span class="n">rargs2</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">rargs</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
    <span class="n">rargs2</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="n">rargs</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>

    <span class="n">rb_obj_instance_eval</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">rargs2</span><span class="p">,</span> <span class="n">rargs</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span> <i class="conum" data-value="2"></i><b>(2)</b>

     <span class="k">return</span> <span class="n">Qnil</span><span class="p">;</span>
 <span class="p">}</span> <span class="cm">/* }}} */</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Then we use the internal symbol  <code>rb_cFile</code> to call <code>File#read</code> on our arguments</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>And then a final eval - see we adhere to the motto!</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="error-handling-3">Error handling</h4>
<div class="paragraph">
<p>Actually we covered this already in the previous section, so nothing to be done here and we better
hurry.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/fun_with_embeddability/meme-white_rabbit.png" alt="Meme White Rabbit">
</div>
<div class="title"><a href="https://knowyourmeme.com/memes/white-rabbit-pointing-at-a-clock" class="bare">https://knowyourmeme.com/memes/white-rabbit-pointing-at-a-clock</a></div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="integrating-javascript">Integrating JavaScript</h3>
<div class="paragraph">
<p>During the 2020s lots of weird things happened and I was forced into my own sort of crisis being stuck
with and on a <a href="https://www.apple.com/os/macos/">macOS</a> for some years.
Needless to say the window management there totally annoyed me and I started another highly
ambitious project aptly named <a href="https://github.com/unexist/touchjs">touchjs</a>.</p>
</div>
<div class="paragraph">
<p>There, I tied the new<sup class="footnote">[<a id="_footnoteref_11" class="footnote" href="#_footnotedef_11" title="View footnote.">11</a>]</sup> <a href="https://support.apple.com/guide/mac-help/use-the-touch-bar-mchlbfd5b039/mac">Touch Bar</a>, basic window management via
<a href="https://github.com/unexist/touchjs">Accessibility API</a> and a JavaScript integration based on <a href="https://duktape.org/">duktape</a> together.</p>
</div>
<div class="sect3">
<h4 id="time-for-screenshots-3">Time for Screenshots</h4>
<div class="paragraph">
<p>Unfortunately we are back at build problems:
Somehow and totally unexplainable to me, I forgot to check in some essential headers to the
project which led to a full-halt:</p>
</div>
<div class="listingblock">
<div class="title">Build attempt #1 of <a href="https://github.com/unexist/touchjs">touchjs</a></div>
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>make
clang <span class="nt">-c</span> <span class="nt">-mmacosx-version-min</span><span class="o">=</span>10.12 <span class="nt">-x</span> objective-c src/touchjs.m <span class="nt">-o</span> src/touchjs.o
src/touchjs.m:17:10: fatal error: <span class="s1">'delegate.h'</span> file not found
   17 | <span class="c">#include "delegate.h"</span>
      |          ^~~~~~~~~~~~
1 error generated.
make: <span class="k">***</span> <span class="o">[</span>src/touchjs.o] Error 1</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">Build attempt #2 of <a href="https://github.com/unexist/touchjs">touchjs</a></div>
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>make
clang <span class="nt">-c</span> <span class="nt">-mmacosx-version-min</span><span class="o">=</span>10.12 <span class="nt">-x</span> objective-c src/touchbar.m <span class="nt">-o</span> src/touchbar.o
src/touchbar.m:57:23: error: use of undeclared identifier <span class="s1">'kQuit'</span>
   57 |     <span class="o">[</span>array addObject: kQuit]<span class="p">;</span>
      |                       ^
src/touchbar.m:149:21: warning: class method <span class="s1">'+presentSystemModalTouchBar:systemTrayItemIdentifier:'</span> not found <span class="o">(</span><span class="k">return </span><span class="nb">type </span>defaults to <span class="s1">'id'</span><span class="o">)</span> <span class="o">[</span><span class="nt">-Wobjc-method-access</span><span class="o">]</span>
  149 |         <span class="o">[</span>NSTouchBar presentSystemModalTouchBar: self.groupTouchBar
      |                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  150 |             systemTrayItemIdentifier: kGroupButton]<span class="p">;</span>
      |             ~~~~~~~~~~~~~~~~~~~~~~~~
src/touchbar.m:150:39: error: use of undeclared identifier <span class="s1">'kGroupButton'</span><span class="p">;</span> did you mean <span class="s1">'kGroupIcon'</span>?
  150 |             systemTrayItemIdentifier: kGroupButton]<span class="p">;</span>
      |                                       ^~~~~~~~~~~~
      |                                       kGroupIcon

<span class="c"># --- %&lt; --- snip --- %&lt; ---</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Fixing something that isn&#8217;t there is quite difficult, and it took me some time and reading
reference manuals to understand what I actually have to restore.
When I made the first progress there, I suddenly remembered I have in fact a backup of the MacBook
Pro from back then.</p>
</div>
<div class="paragraph">
<p>Although I really had fun playing with it, there has never been a real usage of the project.
Luckily I already worked <a href="https://en.wikipedia.org/wiki/Test-driven_development">test-driven</a>, so I can show off these test scripts written in
JavaScript<sup class="footnote">[<a id="_footnoteref_12" class="footnote" href="#_footnotedef_12" title="View footnote.">12</a>]</sup>
along with some resulting shots of the Touch Bar:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/touchjs">touchjs</a>/test/observer.js:1</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="javascript"><span class="cm">/* WM */</span>
<span class="kd">var</span> <span class="nx">wm</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">TjsWM</span><span class="p">();</span> <i class="conum" data-value="1"></i><b>(1)</b>

<span class="nf">tjs_print</span><span class="p">(</span><span class="dl">"</span><span class="s2">wm: trusted=</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">wm</span><span class="p">.</span><span class="nf">isTrusted</span><span class="p">());</span>

<span class="cm">/* Events */</span>
<span class="nx">wm</span><span class="p">.</span><span class="nf">observe</span><span class="p">(</span><span class="dl">"</span><span class="s2">win_open</span><span class="dl">"</span><span class="p">,</span> <span class="nf">function </span><span class="p">(</span><span class="nx">win</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">tjs_print</span><span class="p">(</span><span class="dl">"</span><span class="s2">Open: name=</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">win</span><span class="p">.</span><span class="nf">getTitle</span><span class="p">()</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">, id=</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">win</span><span class="p">.</span><span class="nf">getId</span><span class="p">()</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">, frame=</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">win</span><span class="p">.</span><span class="nf">getFrame</span><span class="p">());</span> <i class="conum" data-value="2"></i><b>(2)</b>
<span class="p">});</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Highly ambitious as I&#8217;ve promised</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Well, just print some details of windows in the normal state</td>
</tr>
</table>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/fun_with_embeddability/touchjs-iterm-observer.png" alt="Screenshot of touchjs (1/3)">
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/fun_with_embeddability/touchjs-touchbar-observer.png" alt="Barshot of touchjs (1/3)">
</div>
<div class="title">Screenshot<sup class="footnote">[<a id="_footnoteref_13" class="footnote" href="#_footnotedef_13" title="View footnote.">13</a>]</sup> of <a href="https://github.com/unexist/touchjs">touchjs/test/observer.js</a> (1/3)</div>
</div>
<div class="paragraph">
<p>And some more with actual UI elements:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/touchjs">touchjs</a>/test/button.js:1</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="javascript"><span class="kd">var</span> <span class="nx">b</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">TjsButton</span><span class="p">(</span><span class="dl">"</span><span class="s2">Test</span><span class="dl">"</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">setBgColor</span><span class="p">(</span><span class="mi">255</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">bind</span><span class="p">(</span><span class="nf">function </span><span class="p">()</span> <span class="p">{</span>
      <span class="nf">tjs_print</span><span class="p">(</span><span class="dl">"</span><span class="s2">Test</span><span class="dl">"</span><span class="p">);</span>
    <span class="p">});</span>

<span class="cm">/* Attach */</span>
<span class="nf">tjs_attach</span><span class="p">(</span><span class="nx">b</span><span class="p">);</span></code></pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/fun_with_embeddability/touchjs-touchbar-button.png" alt="Screenshot of touchjs (2/3)">
</div>
<div class="title">Barshot of <a href="https://github.com/unexist/touchjs">touchjs/test/button.js</a> (2/3)</div>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/touchjs">touchjs</a>/test/widgets.js:1</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="javascript"><span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>

<span class="kd">var</span> <span class="nx">b4</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">TjsButton</span><span class="p">(</span><span class="dl">"</span><span class="s2">Exec</span><span class="dl">"</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">setBgColor</span><span class="p">(</span><span class="mi">255</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">bind</span><span class="p">(</span><span class="nf">function </span><span class="p">()</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">c1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">TjsCommand</span><span class="p">(</span><span class="dl">"</span><span class="s2">ls -l src/</span><span class="dl">"</span><span class="p">);</span>

        <span class="nf">tjs_print</span><span class="p">(</span><span class="nx">c1</span><span class="p">.</span><span class="nf">exec</span><span class="p">().</span><span class="nf">getOutput</span><span class="p">());</span>
    <span class="p">});</span>

<span class="kd">var</span> <span class="nx">s1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">TjsSlider</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">bind</span><span class="p">(</span><span class="nf">function </span><span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
        <span class="nf">tjs_print</span><span class="p">(</span><span class="nx">value</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">%</span><span class="dl">"</span><span class="p">);</span>

        <span class="nx">rgb</span><span class="p">[</span><span class="nx">idx</span><span class="p">]</span> <span class="o">=</span> <span class="nf">parseInt</span><span class="p">(</span><span class="mi">255</span> <span class="o">*</span> <span class="nx">value</span> <span class="o">/</span> <span class="mi">100</span><span class="p">);</span>

        <span class="nx">l1</span><span class="p">.</span><span class="nx">setFgColor</span><span class="p">.</span><span class="nf">apply</span><span class="p">(</span><span class="nx">l1</span><span class="p">,</span> <span class="nx">rgb</span><span class="p">);</span>
    <span class="p">});</span>

<span class="kd">var</span> <span class="nx">sc1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">TjsScrubber</span><span class="p">()</span>
    <span class="p">.</span><span class="nf">attach</span><span class="p">(</span><span class="nx">b1</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">attach</span><span class="p">(</span><span class="nx">b2</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">attach</span><span class="p">(</span><span class="nx">b3</span><span class="p">)</span>
    <span class="p">.</span><span class="nf">attach</span><span class="p">(</span><span class="nx">b4</span><span class="p">);</span>

<span class="cm">/* Attach */</span>
<span class="nf">tjs_attach</span><span class="p">(</span><span class="nx">l1</span><span class="p">);</span>
<span class="nf">tjs_attach</span><span class="p">(</span><span class="nx">sc1</span><span class="p">);</span>
<span class="nf">tjs_attach</span><span class="p">(</span><span class="nx">s1</span><span class="p">);</span></code></pre>
</div>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/fun_with_embeddability/touchjs-touchbar-widgets.png" alt="Screenshot of touchjs (3/3)">
</div>
<div class="title">Barshot of <a href="https://github.com/unexist/touchjs">touchjs/test/widgets.js</a> (2/3)</div>
</div>
</div>
<div class="sect3">
<h4 id="runtime-loading-4">Runtime loading</h4>
<div class="paragraph">
<p>We could go into details here how the loading process and error handling works in Obj-C, but I
ultimately replaced Obj-C with Rust and later on also got rid of the macbook, so interested in how
this can be done in Rust?
Bet you are!</p>
</div>
<div class="paragraph">
<p>Around 2023 I started another pet project under the nice moniker <a href="https://github.com/unexist/rubtle">rubtle</a>.
I can only guess what my plans for it were, but might have been glimpse into the future, but more
on that later, when we talk about the last project of this blog post.
Whatever the plans were, I didn&#8217;t spend too much time on it and rubtle isn&#8217;t polished in any sense.</p>
</div>
<div class="paragraph">
<p>So why do I mention it at all you might ask?
Within rubtle I followed a different approach we haven&#8217;t covered so far.
Instead of inventing an own API, I created bridge<sup class="footnote">[<a id="_footnoteref_14" class="footnote" href="#_footnotedef_14" title="View footnote.">14</a>]</sup> and
allowed the scripts to interact directly with the underlying engine:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/rubtle">rubtle</a>/src/main.rs.js:99</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">args</span><span class="p">:</span> <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nn">env</span><span class="p">::</span><span class="nf">args</span><span class="p">()</span><span class="nf">.collect</span><span class="p">();</span>

    <span class="k">if</span> <span class="mi">1</span> <span class="o">&lt;</span> <span class="n">args</span><span class="nf">.len</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">contents</span> <span class="o">=</span> <span class="nn">fs</span><span class="p">::</span><span class="nf">read_to_string</span><span class="p">(</span><span class="o">&amp;</span><span class="n">args</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span> <i class="conum" data-value="1"></i><b>(1)</b>
        <span class="k">let</span> <span class="n">rubtle</span> <span class="o">=</span> <span class="nn">Rubtle</span><span class="p">::</span><span class="nf">new</span><span class="p">();</span>

        <span class="nf">init_global</span><span class="p">(</span><span class="o">&amp;</span><span class="n">rubtle</span><span class="p">);</span>
        <span class="nf">init_rubtle</span><span class="p">(</span><span class="o">&amp;</span><span class="n">rubtle</span><span class="p">);</span> <i class="conum" data-value="2"></i><b>(2)</b>

        <span class="k">match</span> <span class="n">contents</span> <span class="p">{</span>
            <span class="nf">Ok</span><span class="p">(</span><span class="n">val</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="n">rubtle</span><span class="nf">.eval</span><span class="p">(</span><span class="o">&amp;</span><span class="n">val</span><span class="p">),</span>
            <span class="nf">Err</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="nd">eprintln!</span><span class="p">(</span><span class="s">"File read failed"</span><span class="p">),</span>
        <span class="p">}</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="nd">println!</span><span class="p">(</span><span class="s">"Usage: {}: &lt;JSFile&gt;"</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Just file loading, no surprises here yet</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Now it is getting exciting - off to the next listing!</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/rubtle">rubtle</a>/src/main.rs.js:55</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust"><span class="k">fn</span> <span class="nf">init_rubtle</span><span class="p">(</span><span class="n">rubtle</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Rubtle</span><span class="p">)</span> <span class="p">{</span>
    <span class="nd">#[derive(Default)]</span>
    <span class="k">struct</span> <span class="n">UserData</span> <span class="p">{</span>
        <span class="n">value</span><span class="p">:</span> <span class="nb">i32</span><span class="p">,</span>
    <span class="p">};</span>

    <span class="k">let</span> <span class="k">mut</span> <span class="n">object</span> <span class="o">=</span> <span class="nn">ObjectBuilder</span><span class="p">::</span><span class="o">&lt;</span><span class="n">UserData</span><span class="o">&gt;</span><span class="p">::</span><span class="nf">new</span><span class="p">()</span> <i class="conum" data-value="1"></i><b>(1)</b>
        <span class="nf">.with_constructor</span><span class="p">(|</span><span class="n">inv</span><span class="p">|</span> <span class="p">{</span>
            <span class="k">let</span> <span class="k">mut</span> <span class="n">udata</span> <span class="o">=</span> <span class="n">inv</span><span class="py">.udata</span><span class="nf">.as_mut</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>

            <span class="n">udata</span><span class="py">.value</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">})</span>
        <span class="nf">.with_method</span><span class="p">(</span><span class="s">"inc"</span><span class="p">,</span> <span class="p">|</span><span class="n">inv</span><span class="p">|</span> <span class="k">-&gt;</span> <span class="n">CallbackResult</span><span class="o">&lt;</span><span class="n">Value</span><span class="o">&gt;</span> <span class="p">{</span> <i class="conum" data-value="2"></i><b>(2)</b>
            <span class="k">let</span> <span class="k">mut</span> <span class="n">udata</span> <span class="o">=</span> <span class="n">inv</span><span class="py">.udata</span><span class="nf">.as_mut</span><span class="p">()</span><span class="nf">.unwrap</span><span class="p">();</span>

            <span class="n">udata</span><span class="py">.value</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>

            <span class="nf">Ok</span><span class="p">(</span><span class="nn">Value</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="n">udata</span><span class="py">.value</span><span class="p">))</span>
        <span class="p">})</span>

        <span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>

        <span class="nf">.build</span><span class="p">();</span>

    <span class="n">rubtle</span><span class="nf">.set_global_object</span><span class="p">(</span><span class="s">"Rubtle"</span><span class="p">,</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">object</span><span class="p">);</span> <i class="conum" data-value="3"></i><b>(3)</b>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Using the builder pattern was really a fight back then to me</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Here we are assembling an object by adding some values and methods</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>And we register this as a global object</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Compiled, ready  and armed we can feed this fancy test script into it:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/rubtle">rubtle</a>/test.rs.js:55</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust"><span class="n">var</span> <span class="n">rubtle</span> <span class="o">=</span> <span class="n">new</span> <span class="nf">Rubtle</span><span class="p">();</span>

<span class="n">rubtle</span><span class="nf">.set</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span>
<span class="nf">assert</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="n">rubtle</span><span class="nf">.get</span><span class="p">(),</span> <span class="s">"Damn"</span><span class="p">);</span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="n">rubtle</span><span class="nf">.inc</span><span class="p">();</span>
<span class="nf">assert</span><span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="n">rubtle</span><span class="nf">.get</span><span class="p">(),</span> <span class="s">"Damn"</span><span class="p">);</span>
<span class="nf">print</span><span class="p">(</span><span class="n">rubtle</span><span class="nf">.get</span><span class="p">())</span> <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Seriously no idea..</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ RUSTFLAGS</span><span class="o">=</span><span class="nt">-Awarnings</span> cargo run <span class="nt">--</span> ./test.js
   Compiling rubtle-duktape v0.1.0 <span class="o">(</span>/home/unexist/projects/rubtle/rubtle-duktape<span class="o">)</span> <i class="conum" data-value="1"></i><b>(1)</b>
   Compiling rubtle-lib v0.1.0 <span class="o">(</span>/home/unexist/projects/rubtle/rubtle-lib<span class="o">)</span> <i class="conum" data-value="2"></i><b>(2)</b>
   Compiling rubtle v0.1.0 <span class="o">(</span>/home/unexist/projects/rubtle/rubtle<span class="o">)</span>
    Finished <span class="sb">`</span>dev<span class="sb">`</span> profile <span class="o">[</span>unoptimized + debuginfo] target<span class="o">(</span>s<span class="o">)</span> <span class="k">in </span>2.03s
     Running <span class="sb">`</span>target/debug/rubtle ./test.js<span class="sb">`</span>
&lt;JS&gt; <span class="s2">"6"</span> <i class="conum" data-value="3"></i><b>(3)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The required Rust-C-bindings, a courtesy of <a href="https://github.com/rust-lang/rust-bindgen">bindgen</a></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>This contains the heave and <a href="https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html">unsafe</a> lifting we are going to see next</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Well, 6, yes?</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="error-handling-4">Error handling</h4>
<div class="paragraph">
<p>Inside rubtle-lib is lots of scary stuff and I don&#8217;t want to scare away my dear readers, so the
next excerpt is boiled down and absolutely safe to handle:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/rubtle">rubtle</a>/rutle-lib/src/rubtle.rs:291+777</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust"><span class="k">impl</span> <span class="n">Rubtle</span> <span class="p">{</span>
    <span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>

    <span class="cd">///</span>
    <span class="cd">/// Set value to context and assign a global reachable name</span>
    <span class="cd">///</span>
    <span class="cd">/// # Arguments</span>
    <span class="cd">///</span>
    <span class="cd">/// `name`- Name of the value</span>
    <span class="cd">/// `rval` - The actual value</span>
    <span class="cd">///</span>
    <span class="cd">/// # Example</span>
    <span class="cd">///</span>
    <span class="cd">///     use rubtle_lib::{Rubtle, Value};</span>
    <span class="cd">///</span>
    <span class="cd">///     let rubtle = Rubtle::new();</span>
    <span class="cd">///     let rval = Value::from(4);</span>
    <span class="cd">///</span>
    <span class="cd">///     rubtle.set_global_value("rubtle", &amp;rval);</span>
    <span class="cd">///</span>

    <span class="k">pub</span> <span class="k">fn</span> <span class="nf">set_global_value</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="o">&amp;</span><span class="nb">str</span><span class="p">,</span> <span class="n">rval</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Value</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">unsafe</span> <span class="p">{</span>
            <span class="k">let</span> <span class="n">cstr</span> <span class="o">=</span> <span class="nn">CString</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nf">to_cesu8</span><span class="p">(</span><span class="n">name</span><span class="p">));</span>

            <span class="k">match</span> <span class="n">cstr</span> <span class="p">{</span>
                <span class="nf">Ok</span><span class="p">(</span><span class="n">cval</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="p">{</span>
                    <span class="k">self</span><span class="nf">.push_value</span><span class="p">(</span><span class="n">rval</span><span class="p">);</span> <i class="conum" data-value="1"></i><b>(1)</b>

                    <span class="nn">ffi</span><span class="p">::</span><span class="nf">duk_require_stack</span><span class="p">(</span><span class="k">self</span><span class="py">.ctx</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span> <i class="conum" data-value="2"></i><b>(2)</b>
                    <span class="nn">ffi</span><span class="p">::</span><span class="nf">duk_put_global_lstring</span><span class="p">(</span>
                        <span class="k">self</span><span class="py">.ctx</span><span class="p">,</span>
                        <span class="n">cval</span><span class="nf">.as_ptr</span><span class="p">(),</span>
                        <span class="n">cval</span><span class="nf">.as_bytes</span><span class="p">()</span><span class="nf">.len</span><span class="p">()</span> <span class="k">as</span> <span class="nb">u64</span><span class="p">,</span>
                    <span class="p">);</span>
                <span class="p">}</span>
                <span class="nf">Err</span><span class="p">(</span><span class="n">_</span><span class="p">)</span> <span class="k">=&gt;</span> <span class="nd">unimplemented!</span><span class="p">(),</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>
<span class="p">}</span>

<span class="k">unsafe</span> <span class="k">extern</span> <span class="s">"C"</span> <span class="k">fn</span> <span class="nf">fatal_handler</span><span class="p">(</span><span class="n">_udata</span><span class="p">:</span> <span class="o">*</span><span class="k">mut</span> <span class="nb">c_void</span><span class="p">,</span> <span class="n">msg</span><span class="p">:</span> <span class="o">*</span><span class="k">const</span> <span class="nb">c_char</span><span class="p">)</span> <span class="p">{</span> <i class="conum" data-value="3"></i><b>(3)</b>
    <span class="k">let</span> <span class="n">msg</span> <span class="o">=</span> <span class="nf">from_cesu8</span><span class="p">(</span><span class="nn">CStr</span><span class="p">::</span><span class="nf">from_ptr</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span><span class="nf">.to_bytes</span><span class="p">())</span>
        <span class="nf">.map</span><span class="p">(|</span><span class="n">c</span><span class="p">|</span> <span class="n">c</span><span class="nf">.into_owned</span><span class="p">())</span>
        <span class="nf">.unwrap_or_else</span><span class="p">(|</span><span class="n">_</span><span class="p">|</span> <span class="s">"Failed to decode message"</span><span class="nf">.to_string</span><span class="p">());</span>

    <span class="nd">eprintln!</span><span class="p">(</span><span class="s">"Fatal error from duktape: {}"</span><span class="p">,</span> <span class="n">msg</span><span class="p">);</span>

    <span class="nn">process</span><span class="p">::</span><span class="nf">abort</span><span class="p">();</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Did I mention duktape is also a stack machine and exposes this type of API?</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>This is a similar handling of the stack like we&#8217;ve seen in Lua</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>And we essentially provide a error handler to trap errors when they occur</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="recap-2">Recap</h3>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Ease of use</th>
<th class="tableblock halign-left valign-top">Richness of API</th>
<th class="tableblock halign-left valign-top">Language agnostic</th>
<th class="tableblock halign-left valign-top">Error handling</th>
<th class="tableblock halign-left valign-top">Performance</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Low to complex; depends on the chosen language</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">You provide the API, can be full-fledged interface but also just a simple bridge</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Absolutely, there usually are many bindings and they can also be created with <a href="https://en.wikipedia.org/wiki/Foreign_function_interface">FFI</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Depends a bit on the language, but ranges from easy to complex</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Another thing that depends on the embedder and the embeddee<sup class="footnote">[<a id="_footnoteref_15" class="footnote" href="#_footnotedef_15" title="View footnote.">15</a>]</sup></p></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="webassembly">Webassembly</h2>
<div class="sectionbody">
<div class="paragraph">
<p>I think <a href="https://webassembly.org/">Webassembly</a> is one of the more interesting topics from the web technology
cosmos.
It allows to create binaries from a plethora of languages and to run them mostly at full speed
directly inside stack-based<sup class="footnote">[<a id="_footnoteref_16" class="footnote" href="#_footnotedef_16" title="View footnote.">16</a>]</sup> virtual machines.
Originally meant for embedding in the web, it can also be utilized in other types of software
and provide more flexibility when required, but also raw speed on execution.</p>
</div>
<div class="paragraph">
<p>There is lots of movement and things might <span class="line-through">break</span> change quite often, but
frameworks provide stability here where required.
<a href="https://extism.org/">Extism</a> is such a framework and also the one used in my latest project
<a href="https://github.com/unexist/subtle-rs">subtle-rs</a> as a re-write in Rust and the spiritual successor of subtle.</p>
</div>
<div class="sect2">
<h3 id="screenshots-first-2">Screenshots first</h3>
<div class="paragraph">
<p>subtle-rs is under active development and therefore a piece of a cake to demonstrate it:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/fun_with_embeddability/subtle-rs-0.1.0.png" alt="screenshot of subtle-rs">
</div>
<div class="title">Screenshot of <a href="https://github.com/unexist/subtle-rs">subtle-rs-0.1.0</a></div>
</div>
</div>
<div class="sect2">
<h3 id="runtime-loading-5">Runtime loading</h3>
<div class="paragraph">
<p>In contrast to the other projects, subtle-rs doesn&#8217;t use a scripting language as its config, but
relies on a simple <a href="https://toml.io">TOML</a> file.
Therefore it doesn&#8217;t make sense to go into detail here.
If you still are curious just check the repository: <a href="https://github.com/unexist/subtle-rs/blob/master/subtle.toml" class="bare">https://github.com/unexist/subtle-rs/blob/master/subtle.toml</a></p>
</div>
<div class="paragraph">
<p>Startup and loading the four existing plugins works like a charm:</p>
</div>
<div class="listingblock">
<div class="title">Running of <a href="https://github.com/unexist/subtle-rs">subtle-rs-0.1.0</a></div>
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>cargo run <span class="nt">--</span> <span class="nt">-d</span> :2 <span class="nt">--config-file</span> ./demo.toml
    Finished <span class="sb">`</span>dev<span class="sb">`</span> profile <span class="o">[</span>unoptimized + debuginfo] target<span class="o">(</span>s<span class="o">)</span> <span class="k">in </span>0.12s
     Running <span class="sb">`</span>target/debug/subtle-rs <span class="nt">-d</span> <span class="s1">':2'</span> <span class="nt">--config-file</span> ./demo.toml<span class="sb">`</span>
<span class="o">[</span>2026-01-25T15:48:20Z INFO  subtle_rs] Reading file <span class="sb">`</span><span class="s2">"./demo.toml"</span><span class="s1">'
[2026-01-25T15:48:20Z INFO  subtle_rs] subtle-rs 0.1.0 - Copyright (c) 2025-present Christoph Kappel &lt;christoph@unexist.dev&gt;
[2026-01-25T15:48:20Z INFO  subtle_rs] Released under the GNU GPLv3
[2026-01-25T15:48:20Z INFO  subtle_rs] Compiled for X11
[2026-01-25T15:48:20Z INFO  subtle_rs::display] Display (:2) is 640x480
[2026-01-25T15:48:20Z INFO  subtle_rs::plugin] Loaded plugin (time) <i class="conum" data-value="1"></i><b>(1)</b>
[2026-01-25T15:48:20Z INFO  subtle_rs::plugin] Loaded plugin (fuzzytime) <i class="conum" data-value="2"></i><b>(2)</b>
[2026-01-25T15:48:20Z INFO  subtle_rs::plugin] Loaded plugin (mem) <i class="conum" data-value="3"></i><b>(3)</b>
[2026-01-25T15:48:20Z INFO  subtle_rs::plugin] Loaded plugin (battery) <i class="conum" data-value="4"></i><b>(4)</b>
[2026-01-25T15:48:20Z INFO  subtle_rs::screen] Running on 1 screen(s)</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Written in <a href="https://ziglang.org/">Zig</a> - <a href="https://github.com/unexist/subtle-rs/tree/master/plugins/time" class="bare">https://github.com/unexist/subtle-rs/tree/master/plugins/time</a></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Written in Go - <a href="https://github.com/unexist/subtle-rs/tree/master/plugins/fuzzytime" class="bare">https://github.com/unexist/subtle-rs/tree/master/plugins/fuzzytime</a></td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Written in JavaScript -  <a href="https://github.com/unexist/subtle-rs/tree/master/plugins/mem" class="bare">https://github.com/unexist/subtle-rs/tree/master/plugins/mem</a></td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Written in Rust - <a href="https://github.com/unexist/subtle-rs/tree/master/plugins/battery" class="bare">https://github.com/unexist/subtle-rs/tree/master/plugins/battery</a></td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Under the hood the integration works a bit different to the embeddings before.
The plugins run alone and isolate in their virtual machine and all capabilities beside the ones
provided by the language and the wasm target must be exported by the embedding host.
On the other side, the plugin can also define methods, export them and they can in turn be called
by the host.</p>
</div>
<div class="paragraph">
<p>Creating such exports and load a plugin is quite easy with Extism:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/subtle-rs">subtle-rs</a>/src/plugin.rs:64+84</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust">    <span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>

    <span class="nd">host_fn!</span><span class="p">(</span><span class="nf">get_battery</span><span class="p">(</span><span class="n">_user_data</span><span class="p">:</span> <span class="p">();</span> <span class="n">battery_idx</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">String</span> <span class="p">{</span> <i class="conum" data-value="1"></i><b>(1)</b>
        <span class="k">let</span> <span class="n">charge_full</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fs</span><span class="p">::</span><span class="nf">read_to_string</span><span class="p">(</span>
            <span class="nd">format!</span><span class="p">(</span><span class="s">"/sys/class/power_supply/BAT{}/charge_full"</span><span class="p">,</span> <span class="n">battery_idx</span><span class="p">))</span><span class="o">?</span><span class="p">;</span> <i class="conum" data-value="2"></i><b>(2)</b>
        <span class="k">let</span> <span class="n">charge_now</span> <span class="o">=</span> <span class="nn">std</span><span class="p">::</span><span class="nn">fs</span><span class="p">::</span><span class="nf">read_to_string</span><span class="p">(</span>
            <span class="nd">format!</span><span class="p">(</span><span class="s">"/sys/class/power_supply/BAT{}/charge_now"</span><span class="p">,</span> <span class="n">battery_idx</span><span class="p">))</span><span class="o">?</span><span class="p">;</span>

        <span class="nf">Ok</span><span class="p">(</span><span class="nd">format!</span><span class="p">(</span><span class="s">"{} {}"</span><span class="p">,</span> <span class="n">charge_full</span><span class="nf">.trim</span><span class="p">(),</span> <span class="n">charge_now</span><span class="nf">.trim</span><span class="p">()))</span>
    <span class="p">});</span>

    <span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>

    <span class="k">pub</span><span class="p">(</span><span class="k">crate</span><span class="p">)</span> <span class="k">fn</span> <span class="nf">build</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="n">Plugin</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">url</span> <span class="o">=</span> <span class="k">self</span><span class="py">.url</span><span class="nf">.clone</span><span class="p">()</span><span class="nf">.context</span><span class="p">(</span><span class="s">"Url not set"</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>

        <span class="c1">// Load wasm plugin</span>
        <span class="k">let</span> <span class="n">wasm</span> <span class="o">=</span> <span class="nn">Wasm</span><span class="p">::</span><span class="nf">file</span><span class="p">(</span><span class="n">url</span><span class="nf">.clone</span><span class="p">());</span>
        <span class="k">let</span> <span class="n">manifest</span> <span class="o">=</span> <span class="nn">Manifest</span><span class="p">::</span><span class="nf">new</span><span class="p">([</span><span class="n">wasm</span><span class="p">]);</span>

        <span class="k">let</span> <span class="n">plugin</span> <span class="o">=</span> <span class="nn">extism</span><span class="p">::</span><span class="nn">PluginBuilder</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="o">&amp;</span><span class="n">manifest</span><span class="p">)</span> <i class="conum" data-value="3"></i><b>(3)</b>
            <span class="nf">.with_wasi</span><span class="p">(</span><span class="k">true</span><span class="p">)</span>

            <span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>

            <span class="nf">.with_function</span><span class="p">(</span><span class="s">"get_battery"</span><span class="p">,</span> <span class="p">[</span><span class="n">PTR</span><span class="p">],</span> <span class="p">[</span><span class="n">PTR</span><span class="p">],</span>
                           <span class="nn">UserData</span><span class="p">::</span><span class="nf">default</span><span class="p">(),</span> <span class="k">Self</span><span class="p">::</span><span class="n">get_battery</span><span class="p">)</span> <i class="conum" data-value="4"></i><b>(4)</b>
            <span class="nf">.build</span><span class="p">()</span><span class="o">?</span><span class="p">;</span>

        <span class="nd">debug!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="nd">function_name!</span><span class="p">());</span>

        <span class="nf">Ok</span><span class="p">(</span><span class="n">Plugin</span> <span class="p">{</span>
            <span class="n">name</span><span class="p">:</span> <span class="k">self</span><span class="py">.name</span><span class="nf">.clone</span><span class="p">()</span><span class="nf">.context</span><span class="p">(</span><span class="s">"Name not set"</span><span class="p">)</span><span class="o">?</span><span class="p">,</span>
            <span class="n">url</span><span class="p">,</span>
            <span class="n">interval</span><span class="p">:</span> <span class="k">self</span><span class="py">.interval</span><span class="nf">.unwrap</span><span class="p">(),</span>
            <span class="n">plugin</span><span class="p">:</span> <span class="nn">Rc</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nn">RefCell</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">plugin</span><span class="p">)),</span>
        <span class="p">})</span>
    <span class="p">}</span>

    <span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The macro <code>host_fn!</code> allows us to define functions for our webassembly guest</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Funny how the path of the acpi interface has changed over the years</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Extism also provides an easy-to-use loader</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Time to register our host function</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>And just to complete the usual triplet again, here is what the battery plugin actually does:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/subtle-rs">subtle-rs</a>/plugins/battery/src/lib.rs</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust"><span class="nd">#[host_fn(</span><span class="s">"extism:host/user"</span><span class="nd">)]</span>
<span class="k">extern</span> <span class="s">"ExtismHost"</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">get_battery</span><span class="p">(</span><span class="n">battery_idx</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">String</span><span class="p">;</span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="p">}</span>

<span class="nd">#[plugin_fn]</span> <i class="conum" data-value="2"></i><b>(2)</b>
<span class="k">pub</span> <span class="k">unsafe</span> <span class="k">fn</span> <span class="n">run</span><span class="o">&lt;</span><span class="nv">'a</span><span class="o">&gt;</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="n">FnResult</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">values</span><span class="p">:</span> <span class="nb">String</span> <span class="o">=</span> <span class="k">unsafe</span> <span class="p">{</span> <span class="nf">get_battery</span><span class="p">(</span><span class="s">"0"</span><span class="nf">.into</span><span class="p">())</span><span class="o">?</span> <span class="p">};</span> <i class="conum" data-value="3"></i><b>(3)</b>

    <span class="nd">info!</span><span class="p">(</span><span class="s">"battery {}"</span><span class="p">,</span> <span class="n">values</span><span class="p">);</span>

    <span class="k">let</span> <span class="p">(</span><span class="n">charge_full</span><span class="p">,</span> <span class="n">charge_now</span><span class="p">)</span> <span class="o">=</span> <span class="n">values</span><span class="nf">.split</span><span class="p">(</span><span class="s">" "</span><span class="p">)</span> <i class="conum" data-value="4"></i><b>(4)</b>
        <span class="nf">.filter_map</span><span class="p">(|</span><span class="n">v</span><span class="p">|</span> <span class="n">v</span><span class="py">.parse</span><span class="p">::</span><span class="o">&lt;</span><span class="nb">i32</span><span class="o">&gt;</span><span class="p">()</span><span class="nf">.ok</span><span class="p">())</span>
        <span class="nf">.collect_tuple</span><span class="p">()</span>
        <span class="nf">.or</span><span class="p">(</span><span class="nf">Some</span><span class="p">((</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">)))</span>
        <span class="nf">.unwrap</span><span class="p">();</span>

    <span class="nf">Ok</span><span class="p">(</span><span class="nd">format!</span><span class="p">(</span><span class="s">"{}%"</span><span class="p">,</span> <span class="n">charge_now</span> <span class="o">*</span> <span class="mi">100</span> <span class="o">/</span> <span class="n">charge_full</span><span class="p">))</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This imports the function from the host</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Mark this function for export to the host</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Sadly the <code>unsafe</code> here is required&#8230;&#8203;</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Pretty straight forward - parse and convert with a bit error checking - one line</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="error-handling-5">Error handling</h3>
<div class="paragraph">
<p>Due to the isolation of the plugins the error handling happens inside the virtual machine:</p>
</div>
<div class="listingblock">
<div class="title"><a href="https://github.com/unexist/subtle-rs">subtle-rs</a>/src/plugins.rs:118</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust">    <span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>
<span class="k">impl</span> <span class="n">Plugin</span> <span class="p">{</span>

    <span class="k">pub</span><span class="p">(</span><span class="k">crate</span><span class="p">)</span> <span class="k">fn</span> <span class="nf">update</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="k">let</span> <span class="n">res</span> <span class="o">=</span> <span class="k">self</span><span class="py">.plugin</span><span class="nf">.borrow_mut</span><span class="p">()</span><span class="nf">.call</span><span class="p">(</span><span class="s">"run"</span><span class="p">,</span> <span class="s">""</span><span class="p">)</span><span class="o">?</span><span class="p">;</span> <i class="conum" data-value="1"></i><b>(1)</b>

        <span class="nd">debug!</span><span class="p">(</span><span class="s">"{}: res={}"</span><span class="p">,</span> <span class="nd">function_name!</span><span class="p">(),</span> <span class="n">res</span><span class="p">);</span>

        <span class="nf">Ok</span><span class="p">(</span><span class="n">res</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="cm">/* --- %&lt; --- snip --- %&lt; --- */</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Just a quick call and result check of the plugin function</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="recap-3">Recap</h3>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
<col style="width: 25%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Ease of use</th>
<th class="tableblock halign-left valign-top">Richness of API</th>
<th class="tableblock halign-left valign-top">Language agnostic</th>
<th class="tableblock halign-left valign-top">Error handling</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">Depends on the language, but you can pick from the list of supported ones</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">All noteworthy API must be provided by the host, like time</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Yes, the list of supported language is quite nice</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Extism offers easy integration and error checking</p></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Time for a conclusion after such a marathon through many ideas, languages and projects, so we can
call this a day.
We have seen different approaches of providing an API to essentially shape what a guest or plugin
can do in your application.
And we have also covered error checking and seen how it can range from being arcane and nasty to be
handled entirely by your framework.</p>
</div>
<div class="paragraph">
<p>I think taken with care the integration of scripting languages can be a great way to ease the
hurdle of providing new feature sets.
It can also allow different audiences not familiar with the host language or host domain to enrich
it.
And additionally approaches like webassembly allow to combine the raw processing speed of compiled
languages with the ease-of-use flow of scripting.</p>
</div>
<div class="paragraph">
<p>The list of examples is quite long, but please help yourself:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://github.com/unexist/deskbar" class="bare">https://github.com/unexist/deskbar</a></p>
</li>
<li>
<p><a href="https://github.com/unexist/subtle" class="bare">https://github.com/unexist/subtle</a></p>
</li>
<li>
<p><a href="https://github.com/unexist/touchjs" class="bare">https://github.com/unexist/touchjs</a></p>
</li>
<li>
<p><a href="https://github.com/unexist/rubtle" class="bare">https://github.com/unexist/rubtle</a></p>
</li>
<li>
<p><a href="https://github.com/unexist/subtle-rs" class="bare">https://github.com/unexist/subtle-rs</a></p>
</li>
</ul>
</div>
</div>
</div>
<div id="footnotes">
<hr>
<div class="footnote" id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. Correctness-ily?
</div>
<div class="footnote" id="_footnotedef_2">
<a href="#_footnoteref_2">2</a>. Beside crypto, never do that unless this is your entire business
</div>
<div class="footnote" id="_footnotedef_3">
<a href="#_footnoteref_3">3</a>. ..followed by kilometres of error trace I wasn&#8217;t in the mood to fix right now
</div>
<div class="footnote" id="_footnotedef_4">
<a href="#_footnoteref_4">4</a>. Check <code>mem.c</code> if you are curious
</div>
<div class="footnote" id="_footnotedef_5">
<a href="#_footnoteref_5">5</a>. Technically everything that can be linked, so other languages <em>might</em> actually be possible here. Dare I to try <a href="https://rust-lang.org/">Rust</a>?
</div>
<div class="footnote" id="_footnotedef_6">
<a href="#_footnoteref_6">6</a>. Haven&#8217;t touched Lua for ages, but apparently this is still true.
</div>
<div class="footnote" id="_footnotedef_7">
<a href="#_footnoteref_7">7</a>. There are jokes about how every type in Lua is a table..
</div>
<div class="footnote" id="_footnotedef_8">
<a href="#_footnoteref_8">8</a>. Lua uses negative numbers
</div>
<div class="footnote" id="_footnotedef_9">
<a href="#_footnoteref_9">9</a>. You probably get the pun..
</div>
<div class="footnote" id="_footnotedef_10">
<a href="#_footnoteref_10">10</a>. Well, technically this isn&#8217;t true, if you consider the outdated Ruby version, but..
</div>
<div class="footnote" id="_footnotedef_11">
<a href="#_footnoteref_11">11</a>. To me at least..
</div>
<div class="footnote" id="_footnotedef_12">
<a href="#_footnoteref_12">12</a>. Adding <a href="https://en.wikipedia.org/wiki/Objective-C">Obj-c</a> to this mix is something for another post..
</div>
<div class="footnote" id="_footnotedef_13">
<a href="#_footnoteref_13">13</a>. Just kidding - see next shots!
</div>
<div class="footnote" id="_footnotedef_14">
<a href="#_footnoteref_14">14</a>. Which probably is another API?
</div>
<div class="footnote" id="_footnotedef_15">
<a href="#_footnoteref_15">15</a>. Does this word exist?
</div>
<div class="footnote" id="_footnotedef_16">
<a href="#_footnoteref_16">16</a>. Again a stack machine?
</div>
</div>]]></content><author><name>Christoph Kappel</name></author><category term="tech" /><category term="showcase" /><category term="c" /><category term="rust" /><category term="lua" /><category term="ruby" /><category term="javascript" /><category term="webassembly" /><category term="duktape" /><category term="extism" /><summary type="html"><![CDATA[Personal journey through 20+ years of FOSS-projects under the umbrella of how to embed scripting languages for fun.]]></summary></entry><entry><title type="html">Rewriting in Rust</title><link href="https://unexist.blog/tech/2025/11/09/rewriting-in-rust.html" rel="alternate" type="text/html" title="Rewriting in Rust" /><published>2025-11-09T17:32:00+01:00</published><updated>2025-11-09T17:32:00+01:00</updated><id>https://unexist.blog/tech/2025/11/09/rewriting-in-rust</id><content type="html" xml:base="https://unexist.blog/tech/2025/11/09/rewriting-in-rust.html"><![CDATA[<div class="paragraph">
<p>During my career facing legacy code has always been an annoying task and it took me quite some
years to understand, that oftentimes today&#8217;s code is tomorrow&#8217;s legacy.
Still, legacy code can be a great opportunity to learn something new and especially when you are
the original author of the piece.</p>
</div>
<div class="paragraph">
<p>This post jumps on the bandwagon of rewriting everything in <a href="https://rust-lang.org/">Rust</a> and elaborates a bit on my
personal motivation and learnings of rewriting my pet <a href="https://en.wikipedia.org/wiki/Window_manager">window manager</a> project
<a href="https://subtle.de/">subtle</a>, which I started <a href="https://hg.unexist.dev/subtle/rev/40917ee3b688">~20 years</a><sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup> ago and still use it on a
daily basis.</p>
</div>
<div class="sect1">
<h2 id="why">Why?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Among the many things <a href="https://en.wikipedia.org/wiki/Artificial_intelligence">AI</a> can <em>do for us</em>, migrating code from one language into another is
usually a strong selling point and even without AI there are excellent tools on its own, like
<a href="https://github.com/immunant/c2rust">C2Rust</a>, to get the job done with just a flick of a finger.</p>
</div>
<div class="paragraph">
<p>So why is an excellent question.</p>
</div>
<div class="paragraph">
<p>One of my main motivators isn&#8217;t just to get the job done, like I lamented on a bit in my
<a href="https://unexist.blog/myself/2025/09/22/fear-of-missing-out-on-ai.html">previous blog post</a>, but to have
a learning experience and take something from it besides another code base, which easily ticks
every point of the <a href="https://understandlegacycode.com/blog/what-is-legacy-code-is-it-code-without-tests/">legacy code checklist</a>.</p>
</div>
<div class="paragraph">
<p>Manual labor isn&#8217;t probably the most controversial aspect of it, but porting an <a href="https://en.wikipedia.org/wiki/X_Window_System">X11</a>
application in the <span class="line-through">day</span> <span class="line-through">year</span> epoch of <a href="https://en.wikipedia.org/wiki/Wayland_(protocol)">Wayland</a> might look
like a waste of time.</p>
</div>
<div class="paragraph">
<p>Alas, the reasoning here is basically the same.
Plus I&#8217;ve spent many years with X11 learning its core concepts and <strong>still</strong> like the system and
capabilities.</p>
</div>
<div class="paragraph">
<p>On a side note - I am not entirely certain there is a giant switch to get rid of X11 yet, despite
how decisions of e.g. the <a href="https://blogs.gnome.org/alatiera/2025/06/23/x11-session-removal-faq/">GNOME project</a><sup class="footnote">[<a id="_footnoteref_2" class="footnote" href="#_footnotedef_2" title="View footnote.">2</a>]</sup> might appear.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="learnings-so-far">Learnings so far</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Porting a codebase, like the one of subtle with <em>14728</em> <a href="https://en.wikipedia.org/wiki/Source_lines_of_code">LoC</a> (according to
<a href="https://dwheeler.com/sloccount/">sloccount</a><sup class="footnote">[<a id="_footnoteref_3" class="footnote" href="#_footnotedef_3" title="View footnote.">3</a>]</sup>), brought loads of challenges with it.
Some of them were the usual ones like "where to start" and how can this be done in language X, but
let us concentrate here on a handful of interesting points.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
The problems are inter-related, and it is sometimes a <a href="https://en.wikipedia.org/wiki/Chicken_or_the_egg">chicken or the egg</a>-type
of problem which to address first, so please be prepared to jump a bit around if necessary.
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="god-objects">God objects</h3>
<div class="paragraph">
<p>When I started subtle back then, I didn&#8217;t even know that this pattern is called
<a href="https://en.wikipedia.org/wiki/God_object">God Object</a> or that it is considered to be prime example of an
<a href="https://en.wikipedia.org/wiki/Anti-pattern">anti-pattern</a>.
To me it was something that I&#8217;ve learned by reading other people&#8217;s code and looked like a good
solution to a problem, which is still relevant today.</p>
</div>
<div class="sect3">
<h4 id="problem">Problem</h4>
<div class="paragraph">
<p>The main problem is kind of easy to explain and mainly related to software design:
Your program needs to keep track of data like state or socket descriptors and many related functions
have to access and sometimes mutate them.</p>
</div>
<div class="paragraph">
<p>There are several ways to tackle it, like moving everything related together, but this can also
mean there is basically just one big file and C isn&#8217;t the strongest language to enforce a proper
structure and coherence.
It was way easier to have a global object which included every bit and was available throughout
the program.</p>
</div>
<div class="paragraph">
<p>This might obviously lead to interesting side-effects in multi-threaded applications, but
fortunately the design goal of subtle has always been to be single-threaded and no other means of
locking were required.</p>
</div>
<div class="paragraph">
<p>What I did not understand back then and which is more of concern here, is the implicit coupling of
everything to this god object.
This means changing the god object may require changes of other parts of the program and also may
unknowingly break other parts of the application.</p>
</div>
</div>
<div class="sect3">
<h4 id="solution">Solution</h4>
<div class="paragraph">
<p>subtle-rs (as its predecessor) is event-driven and many parts revolve around a single connection to
the X11 server.
This connection must be available to most parts and moving everything into the holding object made
proper <a href="https://en.wikipedia.org/wiki/Separation_of_concerns}">separation of concerns</a> more difficult.</p>
</div>
<div class="paragraph">
<p>Like every worth-while decision this is a classical <a href="https://en.wikipedia.org/wiki/Trade-off">trade-off</a> and the original design
was kept with the addition to carry the dependency <strong>explicitly</strong> through the codebase.</p>
</div>
<div class="sect4">
<h5 id="c-version">C version</h5>
<div class="listingblock">
<div class="title">subtle/client.c:</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="kt">void</span> <span class="nf">subClientSetSizeHints</span><span class="p">(</span><span class="n">SubClient</span> <span class="o">*</span><span class="n">c</span><span class="p">,</span> <span class="kt">int</span> <span class="o">*</span><span class="n">flags</span><span class="p">)</span> <span class="p">{</span>
<span class="p">...</span>
<span class="p">}</span></code></pre>
</div>
</div>
</div>
<div class="sect4">
<h5 id="rust-version">Rust version</h5>
<div class="listingblock">
<div class="title">subtle-rs/client.rs:</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust"><span class="k">pub</span><span class="p">(</span><span class="k">crate</span><span class="p">)</span> <span class="k">fn</span> <span class="nf">set_size_hints</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">subtle</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Subtle</span><span class="p">,</span> <span class="n">mode_flags</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">ClientFlags</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span> <span class="p">{</span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="o">...</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The signature includes a reference to <code>Subtle</code>.</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="raii">RAII</h3>
<div class="paragraph">
<p><a href="https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization">Resource acquisition is initialization</a> (RAII) is another programming idiom, which is less
of a concern in C-based languages, but can turn into a problem in strict languages like Rust.
Simply put this just means whenever we initialize something like a holding structure, we also have
to initialize all of its members due to the general idea of predictable runtime behavior and
<a href="https://github.com/brianwow/zero-cost-abstraction">zero-cost abstraction</a>.</p>
</div>
<div class="sect3">
<h4 id="problem-2">Problem</h4>
<div class="paragraph">
<p>This easily turns into a problem, whenever the holding structure contains something, that requires
some preparation before it can be initialized - like a socket connection:</p>
</div>
<div class="listingblock">
<div class="title">Problematic C code</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="C">struct Holder {
    Connection *conn;
}

Holder *holder = calloc(1, sizeof(Holder)); <i class="conum" data-value="1"></i><b>(1)</b>

holder.conn = MagicallyOpenConnection(); <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Init the holding structure</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Open the actual connection</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="solution-2">Solution</h4>
<div class="paragraph">
<p>Since this a more general problem in Rust, there exists a bunch of options with different
ergonomics.
One of the easiest ways is to wrap the connection in <a href="https://doc.rust-lang.org/std/option/">Option</a>, which can be initialized
with its default value and set later, but as I&#8217;ve said the ergonomics of mutating<sup class="footnote">[<a id="_footnoteref_4" class="footnote" href="#_footnotedef_4" title="View footnote.">4</a>]</sup> something
on the inside are bothersome.</p>
</div>
<div class="paragraph">
<p>A better <span class="line-through">option</span> alternative is let one of the many cells<sup class="footnote">[<a id="_footnoteref_5" class="footnote" href="#_footnotedef_5" title="View footnote.">5</a>]</sup> handle this job. <a href="https://doc.rust-lang.org/std/cell/struct.OnceCell.html">OnceCell</a>, as the name implies, offers an easy way to
initialize our socket <strong>once</strong> we are prepped.</p>
</div>
<div class="sect4">
<h5 id="c-version-2">C version</h5>
<div class="listingblock">
<div class="title">subtle/subtle.h:</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="k">struct</span> <span class="n">subtle_t</span> <span class="p">{</span>
<span class="p">...</span>
    <span class="n">Display</span> <span class="o">*</span><span class="n">dpy</span><span class="p">;</span> <span class="c1">//&lt; Subtle Xorg display</span>
<span class="p">...</span>
<span class="p">}</span> <span class="n">SubSubtle</span><span class="p">;</span>

<span class="k">extern</span> <span class="n">SubSubtle</span> <span class="o">*</span><span class="n">subtle</span><span class="p">;</span> <i class="conum" data-value="1"></i><b>(1)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>God mode - on!</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="title">subtle/display.c:</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="kt">void</span> <span class="nf">subDisplayInit</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">display</span><span class="p">)</span> <span class="p">{</span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="p">...</span>
    <span class="cm">/* Connect to display and setup error handler */</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="n">subtle</span><span class="o">-&gt;</span><span class="n">dpy</span> <span class="o">=</span> <span class="n">XOpenDisplay</span><span class="p">(</span><span class="n">display</span><span class="p">)))</span> <span class="p">{</span>
<span class="p">...</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>We usually pass the ENV var <code>DISPLAY</code>, but <code>NULL</code> is also an accepted value.</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="title">subtle/subtle.c:</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span> <span class="p">{</span>
<span class="p">...</span>
    <span class="cm">/* Create subtle */</span>
    <span class="n">subtle</span> <span class="o">=</span> <span class="p">(</span><span class="n">SubSubtle</span> <span class="o">*</span><span class="p">)</span> <span class="p">(</span><span class="n">subSharedMemoryAlloc</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">SubSubtle</span><span class="p">)));</span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="p">...</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This is just <code>calloc</code> with some error handling.</td>
</tr>
</table>
</div>
</div>
<div class="sect4">
<h5 id="rust-version-2">Rust version</h5>
<div class="listingblock">
<div class="title">subtle-rs/subtle.rs:</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust"><span class="k">pub</span><span class="p">(</span><span class="k">crate</span><span class="p">)</span> <span class="k">struct</span> <span class="n">Subtle</span> <span class="p">{</span>
<span class="o">...</span>
    <span class="k">pub</span><span class="p">(</span><span class="k">crate</span><span class="p">)</span> <span class="n">conn</span><span class="p">:</span> <span class="n">OnceCell</span><span class="o">&lt;</span><span class="n">RustConnection</span><span class="o">&gt;</span><span class="p">,</span>
<span class="o">...</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="nb">Default</span> <span class="k">for</span> <span class="n">Subtle</span> <span class="p">{</span> <i class="conum" data-value="1"></i><b>(1)</b>
    <span class="k">fn</span> <span class="nf">default</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="n">Subtle</span> <span class="p">{</span>
<span class="o">...</span>
            <span class="n">conn</span><span class="p">:</span> <span class="nn">OnceCell</span><span class="p">::</span><span class="nf">new</span><span class="p">(),</span> <i class="conum" data-value="2"></i><b>(2)</b>
<span class="o">...</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Unfortunately deriving the <a href="https://doc.rust-lang.org/std/default/trait.Default.html">Default trait</a> doesn&#8217;t work for all members of <code>Subtle</code>.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>This initializes our <code>OnceCell</code> with its default value.</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="title">subtle-rs/display.rs:</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust"><span class="k">pub</span><span class="p">(</span><span class="k">crate</span><span class="p">)</span> <span class="k">fn</span> <span class="nf">init</span><span class="p">(</span><span class="n">config</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Config</span><span class="p">,</span> <span class="n">subtle</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">Subtle</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">let</span> <span class="p">(</span><span class="n">conn</span><span class="p">,</span> <span class="n">screen_num</span><span class="p">)</span> <span class="o">=</span> <span class="nn">x11rb</span><span class="p">::</span><span class="nf">connect</span><span class="p">(</span><span class="nf">Some</span><span class="p">(</span><span class="o">&amp;*</span><span class="n">config</span><span class="py">.display</span><span class="p">))</span><span class="o">?</span><span class="p">;</span>
<span class="o">...</span><span class="py">.
    subtle.conn</span><span class="nf">.set</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">();</span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="o">...</span><span class="err">.</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Error handling here would require more explanation, so let us just forget about it and move on.</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="title">subtle-rs/main.rs:</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span> <span class="p">{</span>
<span class="o">...</span>
    <span class="c1">// Init subtle</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">subtle</span> <span class="o">=</span> <span class="nn">Subtle</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="o">&amp;</span><span class="n">config</span><span class="p">);</span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="o">...</span>
    <span class="nn">display</span><span class="p">::</span><span class="nf">init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">config</span><span class="p">,</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">subtle</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>
<span class="o">...</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td><code>Config</code> holds the configured values - a courtesy of <a href="https://docs.rs/clap/latest/clap/index.html">clap</a> - and we convert it with
the help of our <a href="https://docs.rs/clap/latest/clap/index.html">From trait</a> implementation.</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="borrow-checker">Borrow checker</h3>
<div class="paragraph">
<p>Did you wonder why the (in)famous borrow checker isn&#8217;t number one on our list of problems?
Well, simply because you can come pretty far without running into beloved errors like
<a href="https://doc.rust-lang.org/error_codes/E0499.html">E0499</a> or <a href="https://doc.rust-lang.org/error_codes/E0502.html">E0502</a> and grouping problems to keep a common thread is quite
difficult.</p>
</div>
<div class="paragraph">
<p>Anyway, back to the topic at hand: <strong>Why can&#8217;t we just keep a mutable reference of our god object
all the time and pass it around?</strong></p>
</div>
<div class="sect3">
<h4 id="problem-3">Problem</h4>
<div class="paragraph">
<p>Interestingly this is again more about software design and Rust&#8217;s pragmatic way of handling
mutability in contrast to other (functional) languages like <a href="https://www.haskell.org/">Haskell</a>.
Please have a look at the next code block:</p>
</div>
<div class="listingblock">
<div class="title">Problematic Rust code</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust"><span class="nd">#[derive(Default)]</span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="k">struct</span> <span class="n">Counter</span> <span class="p">{</span>
    <span class="n">number</span><span class="p">:</span> <span class="nb">u32</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">Counter</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">increment</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span> <i class="conum" data-value="2"></i><b>(2)</b>
        <span class="k">self</span><span class="py">.number</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">fn</span> <span class="nf">print</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span> <i class="conum" data-value="3"></i><b>(3)</b>
        <span class="nd">println!</span><span class="p">(</span><span class="s">"number={}"</span><span class="p">,</span> <span class="k">self</span><span class="py">.number</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">increment_counter</span><span class="p">(</span><span class="n">counter</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">Counter</span><span class="p">)</span> <span class="p">{</span> <i class="conum" data-value="4"></i><b>(4)</b>
    <span class="n">counter</span><span class="py">.number</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">print_counter</span><span class="p">(</span><span class="n">counter</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">Counter</span><span class="p">)</span> <span class="p">{</span> <i class="conum" data-value="5"></i><b>(5)</b>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"counter={}"</span><span class="p">,</span> <span class="n">counter</span><span class="py">.number</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">counter</span> <span class="o">=</span> <span class="nn">Counter</span><span class="p">::</span><span class="nf">default</span><span class="p">();</span>

    <span class="n">counter</span><span class="nf">.increment</span><span class="p">();</span> <i class="conum" data-value="6"></i><b>(6)</b>
    <span class="n">counter</span><span class="nf">.print</span><span class="p">();</span> <i class="conum" data-value="7"></i><b>(7)</b>

    <span class="nf">increment_counter</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">counter</span><span class="p">);</span> <i class="conum" data-value="8"></i><b>(8)</b>
    <span class="nf">print_counter</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">counter</span><span class="p">);</span> <i class="conum" data-value="9"></i><b>(9)</b>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td><a href="https://doc.rust-lang.org/rust-by-example/trait/derive.html">Derive</a> is one of Rust&#8217;s real work horses.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td><code>Mut</code> required due to write to binding.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Is <code>mut</code> required here?</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td><code>Mut</code>!</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td><code>Mut</code>?</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td>Implied <code>mut</code>!</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>Implied <code>mut</code>?</td>
</tr>
<tr>
<td><i class="conum" data-value="8"></i><b>8</b></td>
<td><code>Mut</code>!</td>
</tr>
<tr>
<td><i class="conum" data-value="9"></i><b>9</b></td>
<td>Why <code>mut</code>?</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>If you don&#8217;t mind trailing all those terribly explicit <code>mut</code> keywords the above code runs fine and
if you don&#8217;t try to re-borrow anything the <a href="https://www.slingacademy.com/article/understanding-borrowing-rules-aliasing-vs-mutability/">aliasing rules</a> work in your favor.</p>
</div>
<div class="paragraph">
<p>A different story is the coupling and the cognitive load:
When everything gets a mutable reference, everything is coupled together and you can never be sure
about the side-effects of calling a certain function.</p>
</div>
</div>
<div class="sect3">
<h4 id="solution-3">Solution</h4>
<div class="paragraph">
<p>The easiest and most naive solution to this kind of problem is just omit <code>mut</code> wherever possible.</p>
</div>
<div class="listingblock">
<div class="title">Adapted Rust code</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust"><span class="nd">#[derive(Default)]</span>
<span class="k">struct</span> <span class="n">Counter</span> <span class="p">{</span>
    <span class="n">number</span><span class="p">:</span> <span class="nb">u32</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">Counter</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">increment</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="py">.number</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">fn</span> <span class="nf">print</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span> <i class="conum" data-value="1"></i><b>(1)</b>
        <span class="nd">println!</span><span class="p">(</span><span class="s">"number={}"</span><span class="p">,</span> <span class="k">self</span><span class="py">.number</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">increment_counter</span><span class="p">(</span><span class="n">counter</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">mut</span> <span class="n">Counter</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">counter</span><span class="py">.number</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">print_counter</span><span class="p">(</span><span class="n">counter</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Counter</span><span class="p">)</span> <span class="p">{</span> <i class="conum" data-value="2"></i><b>(2)</b>
    <span class="nd">println!</span><span class="p">(</span><span class="s">"number={}"</span><span class="p">,</span> <span class="n">counter</span><span class="py">.number</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">counter</span> <span class="o">=</span> <span class="nn">Counter</span><span class="p">::</span><span class="nf">default</span><span class="p">();</span>

    <span class="n">counter</span><span class="nf">.increment</span><span class="p">();</span>
    <span class="n">counter</span><span class="nf">.print</span><span class="p">();</span>

    <span class="nf">increment_counter</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="n">counter</span><span class="p">);</span>
    <span class="nf">print_counter</span><span class="p">(</span><span class="o">&amp;</span><span class="n">counter</span><span class="p">);</span> <i class="conum" data-value="3"></i><b>(3)</b>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This access is just read-only, so no need for <code>mut</code> and also a promises of being side-effect free.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>See &#10102;!</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>See &#10102;!</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="interior-mutability">Interior mutability</h3>
<div class="paragraph">
<p>Now its getting interesting, and we have to talk about given promises of immutability and one more
time about ergonomics of our general design.</p>
</div>
<div class="paragraph">
<p>With the last problem we established the underlying promise of functions, that don&#8217;t require a
mutable reference, will <strong>never</strong> change the object itself and only changes made to a mutable
reference are of any consequence to you.</p>
</div>
<div class="sect3">
<h4 id="problem-4">Problem</h4>
<div class="paragraph">
<p>What happens, when you need to change some internal state, which is just required for internal
bookkeeping and doesn&#8217;t change anything at all for the caller?</p>
</div>
<div class="paragraph">
<p>Have a look at following contrived<sup class="footnote">[<a id="_footnoteref_6" class="footnote" href="#_footnotedef_6" title="View footnote.">6</a>]</sup>
example:</p>
</div>
<div class="listingblock">
<div class="title">Problematic Rust code</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust"><span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">time</span><span class="p">::{</span><span class="n">SystemTime</span><span class="p">,</span> <span class="n">UNIX_EPOCH</span><span class="p">};</span>

<span class="nd">#[derive(Default)]</span>
<span class="k">struct</span> <span class="n">Counter</span> <span class="p">{</span>
    <span class="n">number</span><span class="p">:</span> <span class="nb">u32</span><span class="p">,</span>
    <span class="n">last_printed</span><span class="p">:</span> <span class="nb">u32</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">Counter</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">increment</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="py">.number</span> <span class="o">+=</span> <span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">fn</span> <span class="nf">print</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span> <span class="k">self</span><span class="p">)</span> <span class="p">{</span> <i class="conum" data-value="1"></i><b>(1)</b>
        <span class="k">self</span><span class="py">.last_printed</span> <span class="o">=</span> <span class="nn">SystemTime</span><span class="p">::</span><span class="nf">now</span><span class="p">()</span>
            <span class="nf">.duration_since</span><span class="p">(</span><span class="n">UNIX_EPOCH</span><span class="p">)</span><span class="nf">.unwrap</span><span class="p">()</span><span class="nf">.as_secs</span><span class="p">()</span> <span class="k">as</span> <span class="nb">u32</span><span class="p">;</span> <i class="conum" data-value="2"></i><b>(2)</b>

        <span class="nd">println!</span><span class="p">(</span><span class="s">"number={}"</span><span class="p">,</span> <span class="k">self</span><span class="py">.number</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">counter</span> <span class="o">=</span> <span class="nn">Counter</span><span class="p">::</span><span class="nf">default</span><span class="p">();</span>

    <span class="n">counter</span><span class="nf">.increment</span><span class="p">();</span>
    <span class="n">counter</span><span class="nf">.print</span><span class="p">();</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>To allow our internal bookkeeping the signature must include <code>mut</code> now.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Error checking skipped for brevity - unwrap all the things!</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Here we had to change the methods signature just to allow the pointless action of storing the
last printing time, maybe for big data applications, who knows.</p>
</div>
<div class="paragraph">
<p>From the caller&#8217;s perspective it doesn&#8217;t make any sense to pass a mutable reference into the
<code>print</code> function and from the counter&#8217;s perspective<sup class="footnote">[<a id="_footnoteref_7" class="footnote" href="#_footnotedef_7" title="View footnote.">7</a>]</sup> there wasn&#8217;t any
actual  change of the number.</p>
</div>
</div>
<div class="sect3">
<h4 id="solution-4">Solution</h4>
<div class="paragraph">
<p>This is a pretty common problem and Rust provides many different options like
<a href="https://doc.rust-lang.org/std/cell/">Cell and RefCell</a>, <a href="https://doc.rust-lang.org/core/sync/atomic/index.html">Atomic</a> and some more advanced options like the smart pointer
<a href="https://doc.rust-lang.org/std/sync/struct.Arc.html">Arc</a> for more shenanigans.<sup class="footnote">[<a id="_footnoteref_8" class="footnote" href="#_footnotedef_8" title="View footnote.">8</a>]</sup></p>
</div>
<div class="paragraph">
<p>In our case Cell works splendidly here for our type comes prepared with the
<a href="https://doc.rust-lang.org/std/marker/trait.Copy.html">copy trait</a>:</p>
</div>
<div class="sect4">
<h5 id="c-version-3">C version</h5>
<div class="listingblock">
<div class="title">subtle/subtle.h:</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">subsubtle_t</span> <span class="p">{</span>
<span class="p">...</span>
    <span class="kt">int</span> <span class="n">visible_tags</span><span class="p">;</span> <span class="c1">//&lt; Subtle visible tags</span>
<span class="p">...</span>
<span class="p">}</span> <span class="n">SubSubtle</span><span class="p">;</span></code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">subtle/screen.c:</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="c"><span class="kt">void</span> <span class="nf">subScreenConfigure</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
<span class="p">...</span>
    <span class="cm">/* Reset visible tags, views and available clients */</span>
    <span class="n">subtle</span><span class="o">-&gt;</span><span class="n">visible_tags</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="p">...</span>
    <span class="cm">/* Set visible tags and views to ease lookups */</span>
    <span class="n">subtle</span><span class="o">-&gt;</span><span class="n">visible_tags</span> <span class="o">|=</span> <span class="n">v</span><span class="o">-&gt;</span><span class="n">tags</span><span class="p">;</span>
<span class="p">...</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>No one can stop us from just accessing our god object directly.</td>
</tr>
</table>
</div>
</div>
<div class="sect4">
<h5 id="rust-version-3">Rust version</h5>
<div class="listingblock">
<div class="title">subtle-rs/subtle.rs:</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust"><span class="k">pub</span><span class="p">(</span><span class="k">crate</span><span class="p">)</span> <span class="k">struct</span> <span class="n">Subtle</span> <span class="p">{</span>
<span class="o">...</span>
    <span class="k">pub</span><span class="p">(</span><span class="k">crate</span><span class="p">)</span> <span class="n">visible_tags</span><span class="p">:</span> <span class="n">Cell</span><span class="o">&lt;</span><span class="n">Tagging</span><span class="o">&gt;</span><span class="p">,</span>
<span class="o">...</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="nb">Default</span> <span class="k">for</span> <span class="n">Subtle</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">default</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="n">Subtle</span> <span class="p">{</span>
<span class="o">...</span>
            <span class="n">visible_tags</span><span class="p">:</span> <span class="nn">Cell</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nn">Tagging</span><span class="p">::</span><span class="nf">empty</span><span class="p">()),</span>
<span class="o">...</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">subtle-rs/screen.rs:</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust"><span class="k">pub</span><span class="p">(</span><span class="k">crate</span><span class="p">)</span> <span class="k">fn</span> <span class="nf">configure</span><span class="p">(</span><span class="n">subtle</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Subtle</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="p">()</span><span class="o">&gt;</span> <span class="p">{</span>
<span class="o">...</span>
    <span class="c1">// Reset visible tags, views and available clients</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">visible_tags</span> <span class="o">=</span> <span class="nn">Tagging</span><span class="p">::</span><span class="nf">empty</span><span class="p">();</span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="o">...</span>
    <span class="c1">// Set visible tags and views to ease lookups</span>
    <span class="n">visible_tags</span><span class="nf">.insert</span><span class="p">(</span><span class="n">view</span><span class="py">.tags</span><span class="p">);</span>
<span class="o">...</span>
    <span class="n">subtle</span><span class="py">.visible_tags</span><span class="nf">.replace</span><span class="p">(</span><span class="n">visible_tags</span><span class="p">);</span> <i class="conum" data-value="2"></i><b>(2)</b>
<span class="o">...</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This is a pretty easy case: We introduce a local variable via <a href="https://doc.rust-lang.org/rust-by-example/variable_bindings.html">let binding</a> first.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>And then once we are happy with the result we tell the cell to swap-out the content entirely.</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="explicit-copies">Explicit copies</h3>
<div class="paragraph">
<p>Likewise with mutability, Rust in a similar way annoyingly verbose and explicit with how it handles
data and copies of it.
Seems like keeping all the guarantees and promises there has to be done some work upfront from
every side.</p>
</div>
<div class="sect3">
<h4 id="problem-5">Problem</h4>
<div class="paragraph">
<p>In the next example we just continue with the counter from before, but the repetition of the struct
definition and implementation itself have been removed, since they just divert from the actual
problem:</p>
</div>
<div class="listingblock">
<div class="title">Problematic Rust code</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust"><span class="o">...</span>
<span class="k">fn</span> <span class="nf">print_counter</span><span class="p">(</span><span class="n">counter</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">Counter</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">counter</span><span class="nf">.print</span><span class="p">();</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">let</span> <span class="k">mut</span> <span class="n">counter1</span> <span class="o">=</span> <span class="nn">Counter</span><span class="p">::</span><span class="nf">default</span><span class="p">();</span>

    <span class="n">counter1</span><span class="nf">.increment</span><span class="p">();</span>
<span class="hll">
</span>    <span class="k">let</span> <span class="n">counter2</span> <span class="o">=</span> <span class="n">counter1</span><span class="p">;</span> <i class="conum" data-value="1"></i><b>(1)</b>

    <span class="nf">print_counter</span><span class="p">(</span><span class="o">&amp;</span><span class="n">counter1</span><span class="p">);</span>
    <span class="nf">print_counter</span><span class="p">(</span><span class="o">&amp;</span><span class="n">counter2</span><span class="p">);</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>D&#8217;oh!</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The above snippet fails to compile for apparent reasons, still the error message of the compiler
is kind of a surprise in its detail and content:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="log">error[E0382]: borrow of moved value: `counter1`
  --&gt; src/main.rs:27:19
   |
21 |     let mut counter1 = Counter::default();
   |         ------------ move occurs because `counter1` has type `Counter`, which does not implement the `Copy` trait
...
25 |     let counter2 = counter1;
   |                    -------- value moved here
26 |
27 |     print_counter(&amp;counter1);
   |                   ^^^^^^^^^ value borrowed here after move
   |
note: if `Counter` implemented `Clone`, you could clone the value
  --&gt; src/main.rs:2:1
   |
 2 | struct Counter {
   | ^^^^^^^^^^^^^^ consider implementing `Clone` for this type
...
25 |     let counter2 = counter1;
   |                    -------- you could clone this value

For more information about this error, try `rustc --explain E0382`.
error: could not compile `example` (bin "example") due to 1 previous error</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="solution-5">Solution</h4>
<div class="paragraph">
<p>This is just an example of a really overwhelming and also quite helpful error message from our
partner in crimes - the Rust compiler.
What it points out here is that we can just add the <a href="https://doc.rust-lang.org/std/marker/trait.Copy.html">copy trait marker</a> and also
implement the <a href="https://en.wikipedia.org/wiki/Substructural_type_system#Affine_type_systems">clone trait</a> to satisfy this move.</p>
</div>
<div class="paragraph">
<p>And like our friendly compiler told us, when we just do as suggested the code runs perfectly fine:</p>
</div>
<div class="listingblock">
<div class="title">Fixed Rust code</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="rust"><span class="hll"><span class="nd">#[derive(Default,</span> <span class="nd">Clone,</span> <span class="nd">Copy)]</span>
</span><span class="k">struct</span> <span class="n">Counter</span> <span class="p">{</span>
    <span class="n">number</span><span class="p">:</span> <span class="nb">u32</span><span class="p">,</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>This innocent assignment over there just introduced the concept of move semantics, that Rust uses
internally in its <a href="https://en.wikipedia.org/wiki/Substructural_type_system#Affine_type_systems">affine type system</a>:</p>
</div>
<div class="quoteblock">
<blockquote>
An affine resource can be used at most once, while a linear one must be used exactly once.
</blockquote>
<div class="attribution">
&#8212; <a href="https://en.wikipedia.org/wiki/Substructural_type_system#Affine_type_systems" class="bare">https://en.wikipedia.org/wiki/Substructural_type_system#Affine_type_systems</a>
</div>
</div>
<div class="paragraph">
<p>The definition is quite heavy and somehow unwieldy, but what it basically says, is every type that
doesn&#8217;t come along with a <code>Copy</code> marker trait is moved and the ownership transferred to the
recipient.
All other types are just copied along the way.</p>
</div>
<div class="paragraph">
<p>Accessing the object afterward is a violation of the ownership<sup class="footnote">[<a id="_footnoteref_9" class="footnote" href="#_footnotedef_9" title="View footnote.">9</a>]</sup> model and hence causes such an
error.</p>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Writing this blog post has been an interesting experience on its own and helped me to sharpen my
understanding of how Rust internally works and also helped me to summarize what I actually learned
about it over the course of this project.</p>
</div>
<div class="paragraph">
<p>Porting such a large codebase from my past into a modern language and also re-visiting many of the
taken design choices have been a great experience so far.
And in regard to the legacy code aspect I mentioned initially - there are tests but still even I
don&#8217;t understand some of the odd namings for variables and steps in algorithm anymore.
Maybe I should have read <a href="https://www.goodreads.com/book/show/3735293-clean-code">Clean Code</a> some years earlier <a href="#cleancode">[cleancode]</a>..</p>
</div>
<div class="paragraph">
<p>I currently do not dare to use subtle-rs as my daily window manager yet, mainly because some
required features are still missing like something simple to bring e.g. a clock into the panel, but
I am eagerly looking at <a href="https://extism.org/">Extism</a> for this matter.</p>
</div>
<div class="paragraph">
<p>Naturally I&#8217;ve read some books about Rust if you are looking for inspiration:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://www.goodreads.com/book/show/218011528-idiomatic-rust">Idiomatic Rust: Code like a Rustacean</a> <a href="#idiomaticrust">[idiomaticrust]</a></p>
</li>
<li>
<p><a href="https://www.goodreads.com/book/show/60509158-code-like-a-pro-in-rust">Code Like a Pro in Rust</a> <a href="#coderustpro">[coderustpro]</a></p>
</li>
<li>
<p><a href="https://www.goodreads.com/book/show/221468589-async-rust">Async Rust: Unleashing the Power of Fearless Concurrency</a> <a href="#asyncrust">[asyncrust]</a></p>
</li>
<li>
<p><a href="https://www.goodreads.com/book/show/211161112-effective-rust">https://www.goodreads.com/book/show/211161112-effective-rust</a> <a href="#effectiverust">[effectiverust]</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Most of the examples were taken from following repositories:</p>
</div>
<div class="paragraph">
<p><a href="https://github.com/unexist/subtle" class="bare">https://github.com/unexist/subtle</a><br>
<a href="https://github.com/unexist/subtle-rs" class="bare">https://github.com/unexist/subtle-rs</a></p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="bibliography">Bibliography</h2>
<div class="sectionbody">
<div class="ulist bibliography">
<ul class="bibliography">
<li>
<p><a id="idiomaticrust"></a>[idiomaticrust] Brenden Matthwes, Idiomatic Rust: Code like a Rustacean, Manning 2024</p>
</li>
<li>
<p><a id="coderustpro"></a>[coderustpro] Brenden Matthwes, Code Like a Pro in Rust, Manning 2024</p>
</li>
<li>
<p><a id="asyncrust"></a>[asyncrust] Maxwell Flitton, Caroline Morton, Async Rust: Unleashing the Power of Fearless Concurrency, O&#8217;Reilly 2024</p>
</li>
<li>
<p><a id="effectiverust"></a>[effectiverust] David Drysdale, Effective Rust: 35 Specific Ways to Improve Your Rust Code, O&#8217;Reilly 2024</p>
</li>
<li>
<p><a id="cleancode"></a>[cleancode] Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship, O&#8217;Reilly 2007</p>
</li>
</ul>
</div>
</div>
</div>
<div id="footnotes">
<hr>
<div class="footnote" id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. The first commit is actually a bit older, but apparently moving from CVS &gt; SVN &gt; HG isn&#8217;t frictionless
</div>
<div class="footnote" id="_footnotedef_2">
<a href="#_footnoteref_2">2</a>. GNOME and I had some intermezzos back then, but that is more than a decade ago.
</div>
<div class="footnote" id="_footnotedef_3">
<a href="#_footnoteref_3">3</a>. The underlying model is good way to annoy every developer, but the tool is still nice to count lines.
</div>
<div class="footnote" id="_footnotedef_4">
<a href="#_footnoteref_4">4</a>. I&#8217;ll use <em>mutate</em> instead of <em>write</em> onwards, because I think it better transports the intent.
</div>
<div class="footnote" id="_footnotedef_5">
<a href="#_footnoteref_5">5</a>. More on this later.
</div>
<div class="footnote" id="_footnotedef_6">
<a href="#_footnoteref_6">6</a>. It is really difficult to find an easy example here..
</div>
<div class="footnote" id="_footnotedef_7">
<a href="#_footnoteref_7">7</a>. Yes, I know..
</div>
<div class="footnote" id="_footnotedef_8">
<a href="#_footnoteref_8">8</a>. Interior mutability is sometimes also called escape hatch..
</div>
<div class="footnote" id="_footnotedef_9">
<a href="#_footnoteref_9">9</a>. No post about Rust can be complete without talking about ownership - glad we managed that!
</div>
</div>]]></content><author><name>Christoph Kappel</name></author><category term="tech" /><category term="rust" /><category term="subtle" /><category term="subtle-rs" /><category term="x11" /><category term="clean-code" /><category term="showcase" /><summary type="html"><![CDATA[This blog post describes my experience and learnings during the port of my pet window manager project subtle from C to Rust.]]></summary></entry><entry><title type="html">Fear of missing out on AI</title><link href="https://unexist.blog/myself/2025/09/22/fear-of-missing-out-on-ai.html" rel="alternate" type="text/html" title="Fear of missing out on AI" /><published>2025-09-22T15:48:00+02:00</published><updated>2025-09-22T15:48:00+02:00</updated><id>https://unexist.blog/myself/2025/09/22/fear-of-missing-out-on-ai</id><content type="html" xml:base="https://unexist.blog/myself/2025/09/22/fear-of-missing-out-on-ai.html"><![CDATA[<div class="paragraph">
<p>It has been roughly two years since my last post regarding my experience with the state of AI
(<a href="https://unexist.blog/myself/2023/04/28/coding-with-ai.html">Coding with AI</a>) and I think it is about
time to talk about this again.</p>
</div>
<div class="paragraph">
<p>In contrast to my previous post, I don&#8217;t want to dwell on specific products and tools, but
talk about some points about that I think we should pay close attention to and why this topic
is a generally such a mixed bag to me.</p>
</div>
<div class="paragraph">
<p>Enough chit-chat, let us begin.</p>
</div>
<div class="sect1">
<h2 id="the-journey-so-far">The journey so far</h2>
<div class="sectionbody">
<div class="paragraph">
<p>When I look back at the last two years I can probably safely say the whole thing got even more
traction, as we&#8217;ve left the early adopting phase and AI got a lot of traction.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>AI has entered the mainstream and almost everything gets AI support.</p>
</li>
<li>
<p>Content is specially <a href="https://modelcontextprotocol.io/docs/getting-started/intro">prepared for AI systems</a> apparently without following <a href="https://julsimon.medium.com/why-mcps-disregard-for-40-years-of-rpc-best-practices-will-burn-enterprises-8ef85ce5bc9b">best practices</a>.</p>
</li>
<li>
<p>There is an <a href="https://opentools.ai/tools-today">abundance of new tools</a> and new companies sprout like <a href="https://en.wikipedia.org/wiki/Pop-up_retail">pop-up stores</a>.</p>
</li>
<li>
<p>There are experiments to replace <a href="https://www.forbes.com/sites/rachelwells/2025/03/10/11-jobs-ai-could-replace-in-2025-and-15-jobs-that-are-safe/">general human labor</a> and also specific ones like
<a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC11850350/">nurses in healthcare</a>.</p>
</li>
<li>
<p>There are promises <a href="https://fortune.com/2025/03/27/billionaire-bill-gates-two-day-workweek-ai-replacing-humans/">AI reduces our daily working time</a>.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>So in hindsight everything went according to plan from 2023:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/fear_of_missing_out_on_ai/hype-cycle-for-artificial-intelligence-2023.png" alt="hype cycle for artificial intelligence 2023">
</div>
<div class="title">Gartner <a href="https://www.gartner.com/en/articles/hype-cycle-for-artificial-intelligence" class="bare">https://www.gartner.com/en/articles/hype-cycle-for-artificial-intelligence</a></div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="asking-the-right-questions">Asking the right questions</h2>
<div class="sectionbody">
<div class="paragraph">
<p>I&#8217;ve spent a lot of time reading about the general progression of AI and besides dozen of blog posts
and other articles for and against AI, also some books to get a broader perspective.</p>
</div>
<div class="paragraph">
<p>Among these books are following:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://www.goodreads.com/book/show/204294839-taming-silicon-valley">Taming Silicon Valley: How We Can Ensure That AI Works for Us</a> <a href="#tamingsiliconvalleybook">[tamingsiliconvalleybook]</a></p>
</li>
<li>
<p><a href="https://www.goodreads.com/book/show/217432753-the-ai-con">The AI Con: How to Fight Big Tech&#8217;s Hype and Create the Future We Want</a> <a href="#theaiconbook">[theaiconbook]</a></p>
</li>
<li>
<p><a href="https://www.goodreads.com/book/show/216247514-searches">Searches: Selfood in the Digital Age</a> <a href="#searchesbook">[searchesbook]</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>I somehow begin to wonder what kind of problems are we trying to address really with AI?</p>
</div>
<div class="paragraph">
<p>When I I look at the business side I&#8217;d say the overall themes are <strong>increase of
productivity<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup></strong> like reducing tiresome and/or manual labor and
probably <strong>fear of missing out</strong> competitive advantage.
And on the personal side I mostly see quality-of-life improvements like easy <strong>access to information</strong>
with the help of the natural interfaces like
<a href="https://chatgpt.com/">ChatGPT</a><sup class="footnote">[<a id="_footnoteref_2" class="footnote" href="#_footnotedef_2" title="View footnote.">2</a>]</sup>
and generational parts to create memes and reels more easily.</p>
</div>
<div class="paragraph">
<p>This short list is non-exhaustive mind you, but is sufficient for the points I&#8217;d like to make next.</p>
</div>
<div class="sect2">
<h3 id="increase-of-productivity">Increase of productivity</h3>
<div class="paragraph">
<p>Increasing productivity and reducing work time with technology isn&#8217;t strictly speaking a new idea,
<span class="line-through">blue collar</span> industrial workers faced this already during the mid-19th century
during the <a href="https://en.wikipedia.org/wiki/Industrial_Age">industrial age</a>, but for the first time
<span class="line-through">white collar</span> knowledge workers are impacted, and they are probably not backed by
any <a href="https://en.wikipedia.org/wiki/Labour_movement">labour union</a>.</p>
</div>
<div class="paragraph">
<p>There have been lots of riots and protests according to Wikipedia, so apparently the workforce
wasn&#8217;t all happy with the outcome, but we aren&#8217;t there <strong>yet</strong>, so let us focus on the promise of
improved work-life-balance.</p>
</div>
<div class="paragraph">
<p>Interestingly during that time a strange phenomenon could be observed:</p>
</div>
<div class="quoteblock">
<blockquote>
But rather than allowing a massive reduction of working hours to free the world’s population to
pursue their own projects, pleasures, visions, and ideas, we have seen the ballooning of not even
so much of the ‘service’ sector as of the administrative sector, up to and including the creation
of whole new industries like financial services or telemarketing, or the unprecedented expansion of
sectors like corporate law, academic and health administration, human resources, and public
relations. And these numbers do not even reflect on all those people whose job is to provide
administrative, technical, or security support for these industries, or for that matter the whole
host of ancillary industries (dog-washers, all-night pizza delivery) that only exist because
everyone else is spending so much of their time working in all the other ones.
These are what I propose to call ‘<strong>bullshit jobs<sup class="footnote">[<a id="_footnoteref_3" class="footnote" href="#_footnotedef_3" title="View footnote.">3</a>]</sup></strong>’.
</blockquote>
<div class="attribution">
&#8212; David Graeber<br>
<cite>https://davidgraeber.org/articles/on-the-phenomenon-of-bullshit-jobs-a-work-rant</cite>
</div>
</div>
<div class="paragraph">
<p>This is just an excerpt of an article written for a magazine under the umbrella of things nobody
would print, as the author points out in his book
<a href="https://www.goodreads.com/book/show/34466958-bullshit-jobs">Bullshit Jobs</a> <a href="#bullshitjobsbook">[bullshitjobsbook]</a>,
but still the term hits a mark.
There are a lot more examples and explanations in the book or supposedly bullshit jobs and also
jobs that just feel like one, but my key takeaway is the implied question what can our highly
skilled workforce do for a living, when their field of expertise has been replaced with automatons
and we haven&#8217;t reached a <a href="https://en.wikipedia.org/wiki/Trekonomics">moneyless utopia</a> yet?</p>
</div>
<div class="quoteblock">
<blockquote>
Hell is a collection of individuals who are spending the bulk of their time working on a task they
don&#8217;t like and are not especially good at. Say they were hired because they were excellent
cabinet-makers, and then discover they are expected to spend a great deal of their time frying fish.
</blockquote>
<div class="attribution">
&#8212; David Graeber<br>
<cite>https://davidgraeber.org/articles/on-the-phenomenon-of-bullshit-jobs-a-work-rant</cite>
</div>
</div>
<div class="paragraph">
<p>Another issue I see with increasing productivity is the orientation towards throughput or rather
output in general in information related topics.
Viewed from the business side increasing quantity makes sense to me, this is what the business has
been created for in the first place, but is output the only and ulterior goal and the learning how
to reach and achieve something can be totally neglected?</p>
</div>
</div>
<div class="sect2">
<h3 id="fear-of-missing-out">Fear of missing out</h3>
<div class="paragraph">
<p>If your media feeds are like mine, there is probably something about AI every two or three posts
and depending on the type of media, like e.g. <a href="https://linkedin.com/">LinkedIn</a>, the posts are full
of promises and how the full potential of AI can be unlocked to utilize it for your business.</p>
</div>
<div class="paragraph">
<p>Oftentimes these posts appear to be written with the help of AI and especially
<a href="https://www.youtube.com/watch?v=9Ch4a6ffPZY}">em-dashes</a> enjoy increasing popularity.
I think <a href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food">eating your own dog food</a> is always advisable, so I see no fault there.
On the other hand, I can rarely find some kind of empirical evidence or any other kind of proof
for these theses and here I consider this usually as a red flag - skepticism can help.</p>
</div>
<div class="paragraph">
<p>The current hype and pressure increases and academia has also started looking into the phenomenon of
<a href="https://www.sciencedirect.com/science/article/abs/pii/S0736585325000450">development of fear of missing out on AI</a>.
And there is an increasingly number of posts and voices besides the ones from common
{aibro][AI Bros] who foretell if you don&#8217;t start to use AI today you are going to lose your edge.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
I won&#8217;t cite any of these posts, but if you are curious here is a starter: <a href="https://kagi.com/search?q=use+ai+or+lose" class="bare">https://kagi.com/search?q=use+ai+or+lose</a>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="access-to-information">Access to information</h3>
<div class="paragraph">
<p>Let us start with something positive:
AI does a splendid job of lowering the bar to access information!
Hallucinations vary between dangerous and hilarious and some people are bold enough to state this
is an original <a href="https://pubmed.ncbi.nlm.nih.gov/40038472/">feature of LLM design</a>, but with our previous established
skepticism regarding media consumption this <strong>should</strong> be fine.</p>
</div>
<div class="paragraph">
<p>Delivering probability-based answers to question is only part of the deal, another great application
of these models if for content generation and both goes perfectly hand in hand:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/fear_of_missing_out_on_ai/ai-first.png" alt="ai first">
</div>
<div class="title"><a href="https://marketoonist.com/wp-content/uploads/2023/03/230327.n.aiwritten.jpg" class="bare">https://marketoonist.com/wp-content/uploads/2023/03/230327.n.aiwritten.jpg</a></div>
</div>
<div class="paragraph">
<p>I personally think we should just stick to the bullet point list instead of applying a
"prosa-2-text" conversion twice, but still I wonder what happens to the quality of the information
underneath.
Writing this blog post or generally writing is a really time-consuming task.
Drafting a new post and trying to fill the intended outline with content is a tasks which helps me
personally to pinpoint what I really want to say<sup class="footnote">[<a id="_footnoteref_4" class="footnote" href="#_footnotedef_4" title="View footnote.">4</a>]</sup> and I wouldn&#8217;t
want to miss this journey.</p>
</div>
<div class="paragraph">
<p>I am a bit afraid the following is more than true:</p>
</div>
<div class="quoteblock">
<blockquote>
After all, who are you writing for? Do you care if anybody reads it and how they respond to it?
How can you expect anybody to relate to a piece of writing if it was generated by an AI model?
If you can’t be bothered to write the entire article, you can’t really expect anybody else to be
bothered to read it.
</blockquote>
<div class="attribution">
&#8212; Ben Morris<br>
<cite>https://www.ben-morris.com/ai-and-the-creeping-enshittification-of-work</cite>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="impact-on-society">Impact on society</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This is probably the most interesting point and I think it is really difficult to imagine the
world to come and visionaries like <a href="https://en.wikipedia.org/wiki/Sam_Altman">Sam Altman</a> play a big role in it.
Still, when money gets involved things are sometimes getting sour and I think one of more recent
posts from Altman really condenses the problem down well:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/fear_of_missing_out_on_ai/sam-altman.png" alt="sam altman">
</div>
<div class="title">Sam Altman on <a href="https://x.com/sama/status/195208457436603235">X</a></div>
</div>
<div class="paragraph">
<p>The implied comparison of mass-produced <a href="https://en.wikipedia.org/wiki/Fast_fashion">fast fashion</a> with the overgeneralized idea
of <a href="https://en.wikipedia.org/wiki/Software_as_a_service">Software-as-a-Service</a> is interesting by itself, although I think it is not a good one
to promote your AI services.
For me two of the pain points of fast fashion are the <a href="https://earth.org/fast-fashions-detrimental-effect-on-the-environment/">environmental footprint</a>
and the <a href="https://www.earthday.org/beneath-the-seams-the-human-toll-of-fast-fashion/">exploitation of people</a> in fabric factories and according
to media the same is true for <a href="https://ssir.org/articles/entry/ai-workers-mechanical-turk">the AI industry</a>.
There are many reports of the <a href="https://en.wikipedia.org/wiki/Environmental_impact_of_artificial_intelligence">energy requirements of AI</a> and the references
to the <a href="https://en.wikipedia.org/wiki/Mechanical_Turk">Mechanical Turk</a> are also increasing:</p>
</div>
<div class="quoteblock">
<blockquote>
Amazon using this name [Amazon Mechanical Turk] or their product is surprisingly on the nose:
their system also plays the function of hiding the massive amount of labor needed to make any
modern AI infrastructure work.
<a href="https://en.wikipedia.org/wiki/ImageNet">ImageNet</a>, during its development in the late 2000s, was the largest
single project hosted on the MTurk platform, according to Li.
It took two and a half years and nearly 50,000 workers across 167 countries to create the dataset.
In the end, the data contained over 14 million images, labeled across 22,000 categories.
</blockquote>
<div class="attribution">
&#8212; https://restofworld.org/2025/the-ai-con-book-invisible-labor/
</div>
</div>
<div class="paragraph">
<p>I think the real point he wanted to make is with the help of AI can cheap software be mass-produced
instead of paying monthly fees to service providers or individual solutions to problems.
And this works actually well with software, since there is a negligible impact on the environment
in contrast to physical products.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Currently, I am not exactly sure where we are on the hype cycle from the beginning of this post and I
hope the next few months and years will surely show a direction there.
We are going to see if history repeats itself in the protests of workers and if the dystopian
outlooks of the movie <a href="https://www.imdb.com/title/tt0387808/">Idiocracy</a> stay a work of fiction.</p>
</div>
<div class="paragraph">
<p>I think my personal usage of AI won&#8217;t sky rocket any time soon, since I am most of the time
interested in discovering how and why something can be done and rarely just in a fast solution.
Given the situation that I am interested in exactly that and I don&#8217;t plan on using it beyond this
narrow scope I <span class="line-through">might ask AI</span> would still write it myself.</p>
</div>
<div class="paragraph">
<p>For any other stuff that can readily be automated I totally agree to this:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/fear_of_missing_out_on_ai/joanna-macijewska.png" alt="joanna macijewska">
</div>
<div class="title">Joanna Macijewska on <a href="https://x.com/AuthorJMac/status/1773679197631701238">X</a></div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="bibliography">Bibliography</h2>
<div class="sectionbody">
<div class="ulist bibliography">
<ul class="bibliography">
<li>
<p><a id="tamingsiliconvalleybook"></a>[tamingsiliconvalleybook] Gary F. Marcus, Taming Silicon Valley: How We Can Ensure That AI Works for Us, The MIT Press 2024</p>
</li>
<li>
<p><a id="theaiconbook"></a>[theaiconbook] Emily M. Bender, Alex Hanna, The AI Con: How to Fight Big Tech&#8217;s Hype and Create the Future We Want, Harper 2025</p>
</li>
<li>
<p><a id="searchesbook"></a>[searchesbook] Vauhini Vara, Searches: Selfhood in the Digital Age, Random House 2025</p>
</li>
<li>
<p><a id="stupidityparadoxbook"></a>[stupidityparadoxbook] Mats Alvesson, André Spicer, The Stupidity Paradox: The Power and Pitfalls of Function Stupidity at Work, Profile Books 2016</p>
</li>
<li>
<p><a id="bullshitjobsbook"></a>[bullshitjobsbook] David Graeber, Bullshit Jobs: A Theory, Simon &amp; Schuster 2019</p>
</li>
</ul>
</div>
</div>
</div>
<div id="footnotes">
<hr>
<div class="footnote" id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. Read: getting faster
</div>
<div class="footnote" id="_footnotedef_2">
<a href="#_footnoteref_2">2</a>. Or just "Chatty" as I&#8217;ve learned recently
</div>
<div class="footnote" id="_footnotedef_3">
<a href="#_footnoteref_3">3</a>. Emphasis is mine
</div>
<div class="footnote" id="_footnotedef_4">
<a href="#_footnoteref_4">4</a>. Or rather I am hoping
</div>
</div>]]></content><author><name>Christoph Kappel</name></author><category term="myself" /><category term="ai" /><summary type="html"><![CDATA[This blog post is my personal stance on AI and a glimpse into related questions I rarely see discussed in media.]]></summary></entry><entry><title type="html">Exploring OCI registries</title><link href="https://unexist.blog/tech/2025/06/07/exploring-oci-registries.html" rel="alternate" type="text/html" title="Exploring OCI registries" /><published>2025-06-07T15:16:00+02:00</published><updated>2025-06-07T15:16:00+02:00</updated><id>https://unexist.blog/tech/2025/06/07/exploring-oci-registries</id><content type="html" xml:base="https://unexist.blog/tech/2025/06/07/exploring-oci-registries.html"><![CDATA[<div class="paragraph">
<p>Handling containers is probably something a modern developer can&#8217;t and probably <strong>should</strong> not live
without anymore.
They provide flexibility, allow easy packaging and also sandboxing of stuff you might not want
to have installed on your machine.</p>
</div>
<div class="paragraph">
<p>Like so often in tech, using something successfully doesn&#8217;t imply real understanding how it
works under the hood, but I lived quite happily with this black box and all greasy details
shrouded in mysteries hidden behind tooling like <a href="https://podman.io/">Podman</a>.
This changed, when I started looking for an artifact store for our firmware binary artifacts.
I quickly discovered there are many container registries available, but just a few stores for
<em>ordinary</em> artifacts without spending large parts of our engineering budget on enterprise
license fees.
Passing this question to my bubble lead to a suggestion of a good friend  to have a look at
<a href="https://oras.land/">ORAS</a>, which leverages <a href="https://opencontainers.org/">OCI</a>-compliant registries for exactly what I wanted to
literally archive.
We are already using <a href="https://goharbor.io/">Harbor</a>, so moving other artifacts there as well aroused my interest.</p>
</div>
<div class="paragraph">
<p>So over the course of this article we are going to dive into the container world with a short
primer of the duality of OCI, talk about basic usage and a few advanced points like <a href="https://about.gitlab.com/blog/2022/10/25/the-ultimate-guide-to-sboms/">SBOM</a>
and signing and conclude with my impression on the technology.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
This post includes several introductional chapters as a deep dive into a specific topic.
If you are just here for the examples and how to use the tooling quickly jump ahead and wait for us.
</td>
</tr>
</table>
</div>
<div class="sect1">
<h2 id="what-is-oci">What is OCI?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Turns out the <a href="https://opencontainers.org/">Open Container Initiative (OCI)</a> isn&#8217;t a single spec by itself, but rather
a governance body around several container formats and runtimes - namely:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Runtime Specification (<a href="https://github.com/opencontainers/runtime-spec/blob/main/spec.md">runtime-spec</a>)</p>
</li>
<li>
<p>Image Specification (<a href="https://github.com/opencontainers/image-spec/blob/main/spec.md">image-spec</a>)</p>
</li>
<li>
<p>Distribution Specification (<a href="https://github.com/opencontainers/distribution-spec/blob/main/spec.md">distribution-spec</a>)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The links lead to the related <a href="https://github.com">GitHub</a> projects in case you want to build your own
container engine, but I suggest we focus on <strong>image-spec</strong>, which lays out the structure in all gory
details.</p>
</div>
<div class="sect2">
<h3 id="containers-inside-out">Containers inside out</h3>
<div class="paragraph">
<p>If you&#8217;ve dutifully studied the spec the overall structure of an actual container will probably
not surprise you.
If not believe me, they are less magically than thought, can be fetched with the help of Podman
and easily be dissected on the shell:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman save ghcr.io/oras-project/oras:main <span class="nt">-o</span> oras.tar
Copying blob 08000c18d16d <span class="k">done</span>   |
...
Writing manifest to image destination
<span class="nv">$ </span><span class="nb">tar </span>xvf oras.tar <span class="nt">--one-top-level</span>
08000c18d16dadf9553d747a58cf44023423a9ab010aab96cf263d2216b8b350.tar
...
manifest.json
repositories
<span class="nv">$ </span>tree oras
oras
├── 08000c18d16dadf9553d747a58cf44023423a9ab010aab96cf263d2216b8b350.tar
...
├── 29ec8736648c6f233d234d989b3daed3178a3ec488db0a41085d192d63321c72
    ├── json
    ├── layer.tar -&gt; ../08000c18d16dadf9553d747a58cf44023423a9ab010aab96cf263d2216b8b350.tar
    └── VERSION
...
├── manifest.json
└── repositories

6 directories, 23 files</code></pre>
</div>
</div>
<div class="paragraph">
<p>Following links in <a href="https://www.json.org/">JSON</a>-files and memorizing digests is a bit
cumbersome<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup>, so let us try arrows in a diagram instead.</p>
</div>
</div>
<div class="sect2">
<h3 id="containers-mapped-out">Containers mapped out</h3>
<p><object data='/uml/b0a176789efeadc5b456baa56bcf7279.svg' type='image/svg+xml'  class='plantuml'></object></p>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td><strong>Blobs</strong> is the main directory with all adressable filesystem layers and their related metadata
defined in the appropriate JSON files <em>config</em> and <em>manifest</em>.
<em>The name of the layers are actually digests as well, but to make it easier to follow let us keep the
fancy numbers.</em></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td><strong>Config</strong> contains entries like meta information about author as well as other runtime
information like environment variables, entrypoints, volume mounts etc. as well as infos about
specific hardware architecture and OS.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td><strong>rootfs</strong> contains an ordered list of the digests that compose the actual image.</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>The <strong>manifest</strong> just links to the actual configugration by digest and to the layers.</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>And finally the <strong>index</strong> includes all available manifests and also image annotations.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Mysteries solved, but there is still one essential piece missing - namely media types.</p>
</div>
</div>
<div class="sect2">
<h3 id="what-are-media-types">What are media types?</h3>
<div class="paragraph">
<p>This surprises probably no one, but media types are also covered by a spec
<sup class="footnote">[<a id="_footnoteref_2" class="footnote" href="#_footnotedef_2" title="View footnote.">2</a>]</sup> - the <a href="https://specs.opencontainers.org/image-spec/media-types/">media-spec</a></p>
</div>
<div class="paragraph">
<p>There you can see the exhaustive list of the known types and an implementor&#8217;s todo list for
compliance to the specs.
Conversely, this also means as long as we pick something different we are free to fill layers with
anything to our liking without triggering a certain behaviour accidentally.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="use-cases">Use-Cases</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The next few examples require an OCI-compatible registry and also access to the binaries of
<strong>oras</strong> and <strong>cosign</strong> and some more.
Since installation is usually a hassle, all examples rely on Podman and the well-supported
<a href="https://zotregistry.dev/">Zot Registry</a>.</p>
</div>
<div class="sect2">
<h3 id="firing-up-zot">Firing up Zot</h3>
<div class="paragraph">
<p>Setting up our registry is just a piece of cake and shouldn&#8217;t raise any eyebrows yet.
We pretty much set just the bare essentials - <strong>deliberately</strong> without any hardening for actual
logins.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-it</span> <span class="nt">--name</span> zot-registry <span class="nt">-p</span> 5000:5000 <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
  <span class="nt">-v</span> ./infrastructure/zot-registry/config.json:/etc/zot/config.json <span class="se">\ </span><i class="conum" data-value="1"></i><b>(1)</b>
  ghcr.io/project-zot/zot-linux-amd64:v2.1.2</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Apart from host stuff we also want to enable the fancy <a href="https://zotregistry.dev/v2.1.0/user-guides/user-guide-gui/">web UI</a> and the <a href="https://en.wikipedia.org/wiki/Common_Vulnerabilities_and_Exposures">CVE</a>
scanner - have a glimpse how this can be done on GitHub:<br>
<a href="https://github.com/unexist/showcase-oci-registries/blob/master/infrastructure/zot-registry/config.json" class="bare">https://github.com/unexist/showcase-oci-registries/blob/master/infrastructure/zot-registry/config.json</a></td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Once started and after <a href="https://trivy.dev/latest/">Trivy</a>'s update of the vulnerabilities is done we are dutifully
greeted with an empty list:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/exploring_oci_registries/zot-empty.png" alt="zot empty">
</div>
<div class="title">Zot Registry on <a href="http://localhost:5000" class="bare">http://localhost:5000</a></div>
</div>
<div class="paragraph">
<p>Time to push our first artifact!</p>
</div>
</div>
<div class="sect2">
<h3 id="pushing-a-binary-artifact">Pushing a binary artifact</h3>
<div class="paragraph">
<p>Ultimately I want to push embedded software artifacts to the registry, but since this is public
and my own project <a href="https://github.com/unexist/heos-dial">heos-dial</a> isn&#8217;t ready yet we are pushing a binary of the
<a href="https://go.dev/">Golang</a> version of my faithful <a href="https://unexist.blog/redoc">todo service</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">-it</span> <span class="nt">--network</span><span class="o">=</span>host <span class="se">\ </span><i class="conum" data-value="1"></i><b>(1)</b>
    ghcr.io/oras-project/oras:main <span class="se">\</span>
    push localhost:5000/todo-service:latest <span class="se">\</span>
        <span class="nt">--artifact-type</span> showcase/todo-service <span class="se">\ </span><i class="conum" data-value="2"></i><b>(2)</b>
        <span class="nt">--plain-http</span> <span class="se">\ </span><i class="conum" data-value="3"></i><b>(3)</b>
        todo-service/todo-service.bin:application/octet-stream
✓ Uploaded  todo-service/todo-service.bin                                                                                                                                                                                                            26.1/26.1 MB 100.00%   32ms
  └─ sha256:cc8ab19ee7e1f1f7d43b023317c560943dd2c15448ae77a83641e272bc7a5dbc
✓ Uploaded  application/vnd.oci.empty.v1+json <i class="conum" data-value="4"></i><b>(4)</b>
  └─ sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
✓ Uploaded  application/vnd.oci.image.manifest.v1+json
  └─ sha256:fb1f02fff7f1406ae3aa2d9ebf3f931910b69e99c95e78e211037f11ec8f1eb6
Pushed <span class="o">[</span>registry] localhost:5000/todo-service:latest
ArtifactType: showcase/todo-service
Digest: sha256:fb1f02fff7f1406ae3aa2d9ebf3f931910b69e99c95e78e211037f11ec8f1eb6</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The ORAS container allows us to call it this way and directly pass our arguments.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Here we set our custom artifact type, to be able to distinguish it.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>No need to make our live miserable with SSL/TLS!</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>This isn&#8217;t a <em>real</em> container, so we must provide a <a href="https://oras.land/docs/how_to_guides/manifest_config/" class="bare">https://oras.land/docs/how_to_guides/manifest_config/</a>[dummy config}.</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="pull-it-back">Pull it back</h3>
<div class="paragraph">
<p>One-way-success, time to get it back:</p>
</div>
<div class="sect3">
<h4 id="naively-with-podman">Naively with Podman</h4>
<div class="paragraph">
<p>Pulling images from container registries is one of the core tasks of Podman:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman pull localhost:5000/todo-service:latest
Trying to pull localhost:5000/todo-service:latest...
Error: parsing image configuration: unsupported image-specific operation on artifact with <span class="nb">type</span> <span class="s2">"showcase/todo-service"</span> <i class="conum" data-value="1"></i><b>(1)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Unsurprisingly Podman doesn&#8217;t understand our custom artifact type and hence refuses to do our
bidding.</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>If Podman cannot connect to your local registry and bails out with
<code>http: server gave HTTP response to HTTPS client</code> please make sure to add your <strong>insecure</strong> registry
to your <em>/etc/containers/registries.conf</em> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span><span class="nb">tail</span> <span class="nt">-n2</span> /etc/containers/registries.conf
<span class="o">[</span>registries.insecure]
registries <span class="o">=</span> <span class="o">[</span><span class="s1">'localhost:5000'</span><span class="o">]</span></code></pre>
</div>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="confidently-with-oras">Confidently with ORAS</h4>
<div class="paragraph">
<p>Let us try again - this time with ORAS.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">-it</span> <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
    ghcr.io/oras-project/oras:main <span class="se">\</span>
    pull localhost:5000/todo-service:latest <span class="nt">--plain-http</span>
✓ Pulled      todo-service/todo-service.bin                                                                                                                                                                                                          26.1/26.1 MB 100.00%   38ms
  └─ sha256:cc8ab19ee7e1f1f7d43b023317c560943dd2c15448ae77a83641e272bc7a5dbc
✓ Pulled      application/vnd.oci.image.manifest.v1+json                                                                                                                                                                                               586/586  B 100.00%   66µs
  └─ sha256:fb1f02fff7f1406ae3aa2d9ebf3f931910b69e99c95e78e211037f11ec8f1eb6
Pulled <span class="o">[</span>registry] localhost:5000/todo-service:latest
Digest: sha256:fb1f02fff7f1406ae3aa2d9ebf3f931910b69e99c95e78e211037f11ec8f1eb6
<span class="nv">$ </span>tree todo-service
todo-service
└── todo-service.bin

1 directory, 1 file</code></pre>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="print-information-about-the-image">Print information about the image</h3>
<div class="paragraph">
<p>There are several commands available to gather information about images on the registry.</p>
</div>
<div class="sect3">
<h4 id="fetch-the-manifest">Fetch the manifest</h4>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">-it</span> <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
    ghcr.io/oras-project/oras:main <span class="se">\</span>
    manifest fetch <span class="nt">--pretty</span> <span class="nt">--plain-http</span> <span class="se">\</span>
        localhost:5000/todo-service:latest
<span class="o">{</span>
  <span class="s2">"schemaVersion"</span>: 2,
  <span class="s2">"mediaType"</span>: <span class="s2">"application/vnd.oci.image.manifest.v1+json"</span>,
  <span class="s2">"artifactType"</span>: <span class="s2">"showcase/todo-service"</span>,
  <span class="s2">"config"</span>: <span class="o">{</span>
    <span class="s2">"mediaType"</span>: <span class="s2">"application/vnd.oci.empty.v1+json"</span>, <i class="conum" data-value="1"></i><b>(1)</b>
    <span class="s2">"digest"</span>: <span class="s2">"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"</span>,
    <span class="s2">"size"</span>: 2,
    <span class="s2">"data"</span>: <span class="s2">"e30="</span>
  <span class="o">}</span>,
  <span class="s2">"layers"</span>: <span class="o">[</span>
    <span class="o">{</span>
      <span class="s2">"mediaType"</span>: <span class="s2">"application/octet-stream"</span>,
      <span class="s2">"digest"</span>: <span class="s2">"sha256:cc8ab19ee7e1f1f7d43b023317c560943dd2c15448ae77a83641e272bc7a5dbc"</span>,
      <span class="s2">"size"</span>: 27352532,
      <span class="s2">"annotations"</span>: <span class="o">{</span> <i class="conum" data-value="2"></i><b>(2)</b>
        <span class="s2">"org.opencontainers.image.title"</span>: <span class="s2">"todo-service/todo-service.bin"</span>
      <span class="o">}</span>
    <span class="o">}</span>
  <span class="o">]</span>,
  <span class="s2">"annotations"</span>: <span class="o">{</span>
    <span class="s2">"org.opencontainers.image.created"</span>: <span class="s2">"2025-06-04T11:57:57Z"</span>
  <span class="o">}</span>
<span class="o">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This is our empty dummy config - check the <code>size</code> and <code>data</code> fields.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Annotations are supported as well and can be added with <a href="https://oras.land/docs/how_to_guides/manifest_annotations">oras push --annotation</a>.</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="discover-the-tree">Discover the tree</h4>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">-it</span> <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
    ghcr.io/oras-project/oras:main <span class="se">\</span>
    discover <span class="nt">--format</span> tree <span class="nt">--plain-http</span> <span class="se">\</span>
        localhost:5000/todo-service:latest
localhost:5000/todo-service@sha256:fb1f02fff7f1406ae3aa2d9ebf3f931910b69e99c95e78e211037f11ec8f1eb6</code></pre>
</div>
</div>
<div class="paragraph">
<p>There are many more helpful <a href="https://oras.land/docs/category/oras-commands">commands</a> that can be used to interact with stored images,
other types of <a href="https://de.wikipedia.org/wiki/Binary_Large_Object">blobs</a> and also with supporting files.
Typically among these supporting are museum-less <a href="https://helm.sh/">Helm</a>-charts and also <a href="https://about.gitlab.com/blog/2022/10/25/the-ultimate-guide-to-sboms/">SBOM</a>.</p>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="what-is-an-sbom">What is an SBOM?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>A <strong>software bill of materials</strong> or SBOM is a kind of inventory list of an artifact, which details
included software components and assists in securing the software supply chain.
This gets more and more attention as it should especially since the
<a href="https://en.wikipedia.org/wiki/Log4Shell">log4j vulnerability</a> back then in 2020 and 2021.</p>
</div>
<div class="paragraph">
<p>There are different formats for SBOM files like <a href="https://en.wikipedia.org/wiki/Software_Package_Data_Exchange">SPDX</a> or <a href="https://cyclonedx.org/">CycloneDX</a> and also
a broad range of tools that support one or more of them as input and output is available.</p>
</div>
<div class="paragraph">
<p>I am kind of fond<sup class="footnote">[<a id="_footnoteref_3" class="footnote" href="#_footnotedef_3" title="View footnote.">3</a>]</sup> of Anchore with their tools
<a href="https://github.com/anchore/syft">syft</a> and <a href="https://github.com/anchore/grype">grype</a> and therefore the next examples are going to make use of both of
them.</p>
</div>
<div class="sect2">
<h3 id="syfting-through">Syfting through</h3>
<div class="paragraph">
<p>Since my todo service is based on Golang syft can easily scan the source code and assemble our
SBOM</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">-it</span> <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
    <span class="nt">-v</span> ./todo-service:/in <span class="se">\</span>
    docker.io/anchore/syft:latest <span class="se">\</span>
        scan <span class="nb">dir</span>:/in <span class="nt">-o</span> cyclonedx-json<span class="o">=</span>/workspace/sbom.json <i class="conum" data-value="1"></i><b>(1)</b>
 ✔ Indexed file system                                                                                                                                                                                                                                                    /in
 ✔ Cataloged contents                                                                                                                                                                                        86121fea66864109267c361a1fec880ab49dc5f619205b1f364ecb7ba31eb066
   ├── ✔ Packages                        <span class="o">[</span>70 packages]
   ├── ✔ Executables                     <span class="o">[</span>1 executables]
   ├── ✔ File digests                    <span class="o">[</span>1 files]
   └── ✔ File metadata                   <span class="o">[</span>1 locations]
<span class="o">[</span>0000]  WARN no explicit name and version provided <span class="k">for </span>directory <span class="nb">source</span>, deriving artifact ID from the given path <span class="o">(</span>which is not ideal<span class="o">)</span>
A newer version of syft is available <span class="k">for </span>download: 1.26.1 <span class="o">(</span>installed version is 1.26.0<span class="o">)</span> <i class="conum" data-value="2"></i><b>(2)</b>
<span class="nv">$ </span><span class="nb">cat </span>sbom.json | jq <span class="s1">'.components | length'</span> <i class="conum" data-value="3"></i><b>(3)</b>
71</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>My pick is entirely based on the cool name though.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Interesting since I am using the <code>latest</code> tag.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Quite a lot of components..</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="scanning-for-vulnerabilities">Scanning for vulnerabilities</h3>
<div class="paragraph">
<p>Like Trivy, grype can easily scan from inside a container and provide machine-readable statistics
by default:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">-it</span> <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
    docker.io/anchore/grype:latest <span class="se">\</span>
        sbom:/workspace/sbom.json
 ✔ Vulnerability DB                <span class="o">[</span>updated]
 ✔ Scanned <span class="k">for </span>vulnerabilities     <span class="o">[</span>9 vulnerability matches]
   ├── by severity: 1 critical, 2 high, 6 medium, 0 low, 0 negligible
   └── by status:   9 fixed, 0 not-fixed, 0 ignored
NAME                        INSTALLED  FIXED-IN  TYPE       VULNERABILITY        SEVERITY  EPSS%  RISK
golang.org/x/crypto         v0.15.0    0.17.0    go-module  GHSA-45x7-px36-x8w8  Medium    98.45   36.5
golang.org/x/net            v0.18.0    0.23.0    go-module  GHSA-4v7x-pqxf-cx7m  Medium    98.35   33.4
golang.org/x/crypto         v0.15.0    0.31.0    go-module  GHSA-v778-237x-gjrc  Critical  96.91   32.6
google.golang.org/protobuf  v1.31.0    1.33.0    go-module  GHSA-8r3f-844c-mc37  Medium    46.14    0.1
github.com/jackc/pgx/v5     v5.4.3     5.5.4     go-module  GHSA-mrww-27vc-gghv  High      38.06    0.1
golang.org/x/crypto         v0.15.0    0.35.0    go-module  GHSA-hcg3-q754-cr77  High      15.90  &lt; 0.1
golang.org/x/net            v0.18.0    0.38.0    go-module  GHSA-vvgc-356p-c3xw  Medium     5.05  &lt; 0.1
golang.org/x/net            v0.18.0    0.36.0    go-module  GHSA-qxp5-gwg8-xv66  Medium     1.24  &lt; 0.1
github.com/jackc/pgx/v5     v5.4.3     5.5.2     go-module  GHSA-fqpg-rq76-99pq  Medium      N/A    N/A</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="attaching-our-sbom">Attaching our SBOM</h3>
<div class="paragraph">
<p>If we are content with the scanning result<sup class="footnote">[<a id="_footnoteref_4" class="footnote" href="#_footnotedef_4" title="View footnote.">4</a>]</sup> let us quickly add
this to our image:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">-it</span> <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
    ghcr.io/oras-project/oras:main <span class="se">\</span>
    attach localhost:5000/todo-service:latest <span class="nt">--plain-http</span> <span class="se">\</span>
        <span class="nt">--artifact-type</span> showcase/sbom <span class="se">\ </span><i class="conum" data-value="1"></i><b>(1)</b>
        sbom.json:application/vnd.cyclonedx+json
✓ Uploaded  sbom.json                                                                                                                                                                                                                                50.1/50.1 KB 100.00%    2ms
  └─ sha256:0690e255a326ee93c96bf1471586bb3bc720a1f660eb1c2ac64bbf95a1bd9693
✓ Exists    application/vnd.oci.empty.v1+json                                                                                                                                                                                                              2/2  B 100.00%     0s
  └─ sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a
✓ Uploaded  application/vnd.oci.image.manifest.v1+json                                                                                                                                                                                                 724/724  B 100.00%    3ms
  └─ sha256:5c6bb144aaed7d3e4eb58ac6bcdbf2a68d0409d5328f81c9d413e9301e2517a9
Attached to <span class="o">[</span>registry] localhost:5000/todo-service@sha256:fb1f02fff7f1406ae3aa2d9ebf3f931910b69e99c95e78e211037f11ec8f1eb6
Digest: sha256:5c6bb144aaed7d3e4eb58ac6bcdbf2a68d0409d5328f81c9d413e9301e2517a9</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This gave me a bit of a headache, because Zot supports SBOM scanning and also propagates the results
on the web UI - see the sidepanel for more information.</td>
</tr>
</table>
</div>
<div class="sidebarblock">
<div class="content">
<div class="title">SBOM handling in Zot</div>
<div class="paragraph">
<p>Unfortunately Zot or rather its internal handling of Trivy just allows scans of known media types
and doesn&#8217;t rely on any specific media type to identify passed SBOM files:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="golang"><span class="c">// https://github.com/project-zot/zot/blob/main/pkg/extensions/search/cve/trivy/scanner.go#L278</span>
<span class="k">func</span> <span class="p">(</span><span class="n">scanner</span> <span class="n">Scanner</span><span class="p">)</span> <span class="n">isManifestScanable</span><span class="p">(</span><span class="n">digestStr</span> <span class="kt">string</span><span class="p">)</span> <span class="p">(</span><span class="kt">bool</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
<span class="o">...</span>
    <span class="k">switch</span> <span class="n">imageLayer</span><span class="o">.</span><span class="n">MediaType</span> <span class="p">{</span>
<span class="hll">    <span class="k">case</span> <span class="n">ispec</span><span class="o">.</span><span class="n">MediaTypeImageLayerGzip</span><span class="p">,</span> <span class="n">ispec</span><span class="o">.</span><span class="n">MediaTypeImageLayer</span><span class="p">,</span> <span class="kt">string</span><span class="p">(</span><span class="n">regTypes</span><span class="o">.</span><span class="n">DockerLayer</span><span class="p">)</span><span class="o">:</span> <i class="conum" data-value="1"></i><b>(1)</b>
</span>        <span class="k">continue</span>
    <span class="k">default</span><span class="o">:</span>
    <span class="k">return</span> <span class="no">false</span><span class="p">,</span> <span class="n">zerr</span><span class="o">.</span><span class="n">ErrScanNotSupported</span>
<span class="o">...</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This relies on borrowed definitions from our known <a href="https://github.com/opencontainers/image-spec/blob/main/specs-go/v1/mediatype.go#L43">image-spec</a> as well as
<a href="https://github.com/google/go-containerregistry/blob/main/pkg/v1/types/types.go#L37">go-containerregistry</a>.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>I thought I just got the type wrong, since many pages I&#8217;ve read were a bit vague if it is
<code>example/sbom</code> or <code>sbom/example</code>.
After several hours I found a pending issue which is kind of related to my problem, but the
timestamp of the issue doesn&#8217;t look promising though.
I&#8217;ll put patch-work on my todo list<sup class="footnote">[<a id="_footnoteref_5" class="footnote" href="#_footnotedef_5" title="View footnote.">5</a>]</sup> so I might bring
this forward.</p>
</div>
<div class="paragraph">
<p><a href="https://github.com/project-zot/zot/issues/2415" class="bare">https://github.com/project-zot/zot/issues/2415</a></p>
</div>
</div>
</div>
</div>
<div class="sect2">
<h3 id="discover-our-changes">Discover our changes</h3>
<div class="paragraph">
<p>And if we run discover again we can see there is a new layer:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">-it</span> <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
    ghcr.io/oras-project/oras:main <span class="se">\</span>
    discover <span class="nt">--format</span> tree <span class="nt">--plain-http</span> <span class="se">\</span>
        localhost:5000/todo-service:latest
localhost:5000/todo-service@sha256:fb1f02fff7f1406ae3aa2d9ebf3f931910b69e99c95e78e211037f11ec8f1eb6
└── showcase/sbom
    └── sha256:5c6bb144aaed7d3e4eb58ac6bcdbf2a68d0409d5328f81c9d413e9301e2517a9
        └── <span class="o">[</span>annotations]
            └── org.opencontainers.image.created: <span class="s2">"2025-06-04T12:40:38Z"</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Speaking about security:
Just adding images without means of verification if this is the real deal apart from the checksum
doesn&#8217;t make too much sense too me.</p>
</div>
<div class="paragraph">
<p>I think the why should be clear, let us talk about how.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="image-signing">Image signing</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Needless to say topics like encryption, signatures etc. are usually pretty complicated, so I
can gladly there exists lots of tooling to ease this for us dramatically.
I did the homework for us  in preparation for this post and checked our options.
While doing that I found lots of references to <a href="https://github.com/notaryproject/notary">notary</a> and {skopeo[skopeo], but the full
package and overall documentation of <a href="https://github.com/sigstore/cosign">cosign</a> just convinced me and it can basically sign
anything in a registry.</p>
</div>
<div class="paragraph">
<p>In this last chapter we are going to sign our image and specific layers via
<a href="https://in-toto.io/">in-toto attestations</a> with the help of cosign.</p>
</div>
<div class="sect2">
<h3 id="signing-the-image">Signing the image</h3>
<div class="paragraph">
<p>Cosign comes with lots of useful commands to create and manage identities, signatures and whatnot,
but in the most convenient way it just allows us to select from a list of supported
<a href="https://en.wikipedia.org/wiki/Identity_provider">identity provider</a> in our browser per runtime:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
    ghcr.io/sigstore/cosign/cosign:v2.4.1 <span class="se">\</span>
    sign <span class="nt">--yes</span> <span class="se">\</span>
        localhost:5000/todo-service:latest
Generating ephemeral keys...
Retrieving signed certificate...
Non-interactive mode detected, using device flow.
Enter the verification code xxxx <span class="k">in </span>your browser at: https://oauth2.sigstore.dev/auth/device?user_code<span class="o">=</span>xxxx <i class="conum" data-value="1"></i><b>(1)</b>
Code will be valid <span class="k">for </span>300 seconds
Token received!
Successfully verified SCT...
...
By typing <span class="s1">'y'</span>, you attest that <span class="o">(</span>1<span class="o">)</span> you are not submitting the personal data of any other person<span class="p">;</span> and <span class="o">(</span>2<span class="o">)</span> you understand and agree to the statement and the Agreement terms at the URLs listed above. <i class="conum" data-value="2"></i><b>(2)</b>
tlog entry created with index: 230160511
Pushing signature to: localhost:5000/todo-service</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Quickly follow the link and pick one of your liking - we continue with Github here.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Glad we added <code>--yes</code> - interactivity in container is usually a pain.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>And when we check the web UI we can see there is a bit of progress:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/exploring_oci_registries/zot-signed.png" alt="zot signed">
</div>
<div class="title">Zot Registry on <a href="http://localhost:5000" class="bare">http://localhost:5000</a></div>
</div>
<div class="paragraph">
<p>Relying on Zot is nice and good, but there are other ways to do that.</p>
</div>
</div>
<div class="sect2">
<h3 id="verification-of-the-image">Verification of the image</h3>
<div class="paragraph">
<p>It all boils down to another simple call of cosign:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
    ghcr.io/sigstore/cosign/cosign:v2.4.1 <span class="se">\</span>
    verify  <span class="se">\</span>
        <span class="nt">--certificate-oidc-issuer</span><span class="o">=</span>https://github.com/login/oauth <span class="se">\ </span><i class="conum" data-value="1"></i><b>(1)</b>
        <span class="nt">--certificate-identity</span><span class="o">=</span>christoph@unexist.dev <span class="se">\</span>
        localhost:5000/todo-service:latest | jq <span class="s2">".[] | .critical"</span> <i class="conum" data-value="2"></i><b>(2)</b>
Verification <span class="k">for </span>localhost:5000/todo-service:latest <span class="nt">--</span>
The following checks were performed on each of these signatures: <i class="conum" data-value="3"></i><b>(3)</b>
  - The cosign claims were validated
  - Existence of the claims <span class="k">in </span>the transparency log was verified offline
  - The code-signing certificate was verified using trusted certificate authority certificates
<span class="o">{</span>
  <span class="s2">"identity"</span>: <span class="o">{</span>
    <span class="s2">"docker-reference"</span>: <span class="s2">"localhost:5000/todo-service"</span>
  <span class="o">}</span>,
  <span class="s2">"image"</span>: <span class="o">{</span>
    <span class="s2">"docker-manifest-digest"</span>: <span class="s2">"sha256:fb1f02fff7f1406ae3aa2d9ebf3f931910b69e99c95e78e211037f11ec8f1eb6"</span>
  <span class="o">}</span>,
  <span class="s2">"type"</span>: <span class="s2">"cosign container image signature"</span>
<span class="o">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>There are several options for verification available - we just rely on issuer and mail.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Apparently this <em>critical</em> is nothing of concern and a
<a href="https://www.redhat.com/en/blog/container-image-signing">format specificed by RedHat</a>.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>This is a short summary of the checks that have been performed during the verification.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Just as a negative test this is how it looks like when the verification actually fails:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
    ghcr.io/sigstore/cosign/cosign:v2.4.1 <span class="se">\</span>
    verify  <span class="se">\</span>
        <span class="nt">--certificate-oidc-issuer</span><span class="o">=</span>https://github.com/login/oauth <span class="se">\</span>
        <span class="nt">--certificate-identity</span><span class="o">=</span>anon@anon.rs <span class="se">\</span>
        localhost:5000/todo-service:latest
Error: no matching signatures: none of the expected identities matched what was <span class="k">in </span>the certificate, got subjects <span class="o">[</span>christoph@unexist.dev] with issuer https://github.com/login/oauth
main.go:69: error during <span class="nb">command </span>execution: no matching signatures: none of the expected identities matched what was <span class="k">in </span>the certificate, got subjects <span class="o">[</span>christoph@unexist.dev] with issuer https://github.com/login/oauth</code></pre>
</div>
</div>
<div class="paragraph">
<p>First step done - step two is to sign our SBOM as well.</p>
</div>
</div>
<div class="sect2">
<h3 id="create-an-in-toto-attestation">Create an in-toto attestation</h3>
<div class="paragraph">
<p>If you have made it this far in this post I probably shouldn&#8217;t bore you with another
<a href="https://github.com/in-toto/attestation/blob/main/spec/README.md#in-toto-attestation-framework-spec">spec</a> about in-toto or the framework around it and just provide the
examples:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ DIGEST</span><span class="o">=</span><span class="sb">`</span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">-it</span> <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
    ghcr.io/oras-project/oras:main <span class="se">\</span>
    discover <span class="nt">--format</span> json <span class="nt">--plain-http</span> <span class="se">\</span>
        localhost:5000/todo-service:latest | jq <span class="nt">-r</span> <span class="s2">".referrers[].reference"</span><span class="sb">`</span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
    ghcr.io/sigstore/cosign/cosign:v2.4.1 <span class="se">\</span>
    attest <span class="nt">--yes</span> <span class="se">\ </span><i class="conum" data-value="2"></i><b>(2)</b>
        <span class="nt">--type</span> cyclonedx <span class="se">\ </span><i class="conum" data-value="3"></i><b>(3)</b>
        <span class="nt">--predicate</span> /workspace/sbom.json <span class="se">\</span>
        <span class="nv">$DIGEST</span>
Generating ephemeral keys...
Retrieving signed certificate...
Non-interactive mode detected, using device flow.
Enter the verification code xxxx <span class="k">in </span>your browser at: https://oauth2.sigstore.dev/auth/device?user_code<span class="o">=</span>xxxx
Code will be valid <span class="k">for </span>300 seconds
Token received!
Successfully verified SCT...
Using payload from: /workspace/sbom.json
...
By typing <span class="s1">'y'</span>, you attest that <span class="o">(</span>1<span class="o">)</span> you are not submitting the personal data of any other person<span class="p">;</span> and <span class="o">(</span>2<span class="o">)</span> you understand and agree to the statement and the Agreement terms at the URLs listed above.
using ephemeral certificate:
<span class="nt">-----BEGIN</span> CERTIFICATE-----
LOREMIPSUMDOLORSITAMETCONSECTETURADIPISCINGELIT
MORBIIDSODALESESTVIVAMUSVOLUTPATSODALESTINCIDUNT
...
<span class="nt">-----END</span> CERTIFICATE-----

tlog entry created with index: 232176597</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>We need the digest to identify our artifact for the next steps - so please keep it at hand.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Don&#8217;t forget to deal with the interactive prompt here.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Some information about type and name of what cosign is supposed to attest.</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
cosign still supports the older command <a href="https://github.com/sigstore/cosign/blob/main/doc/cosign_attach_sbom.md">attach sbom</a> to attach artifacts,
but the it is deprecated and it is generally advised to use proper attestations.
There is a heaty debate about its status and maturity though.
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="download-attestation">Download attestation</h3>
<div class="paragraph">
<p>As mentiond before this is complex, so let us have a closer look at what we can actually get back.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
    ghcr.io/sigstore/cosign/cosign:v2.4.1 <span class="se">\</span>
    download attestation <span class="se">\</span>
        <span class="nv">$DIGEST</span> | jq <span class="s2">"del(.payload)"</span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="o">{</span>
  <span class="s2">"payloadType"</span>: <span class="s2">"application/vnd.in-toto+json"</span>, <i class="conum" data-value="2"></i><b>(2)</b>
  <span class="s2">"signatures"</span>: <span class="o">[</span>
    <span class="o">{</span>
      <span class="s2">"keyid"</span>: <span class="s2">""</span>,
      <span class="s2">"sig"</span>: <span class="s2">"MEYCIQDE4/CeQstLjHLE+ZQ+BCH+aaw2wSWSr9i26d7iuazXrwIhAPtly5XBD6C14s/78vTjuHdLOjj2a9TeSgs0yD6YRrZd"</span>
    <span class="o">}</span>
  <span class="o">]</span>
<span class="o">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>We omit the payload data here - feel free to dump your own base64 blob</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>This is the actual type of the payload that has been transmitted.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>If you want to see the actual content of the payload here is a small exercise for you:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
    ghcr.io/sigstore/cosign/cosign:v2.4.1 <span class="se">\</span>
    download attestation <span class="se">\</span>
        <span class="nv">$DIGEST</span> | jq <span class="nt">-r</span> .payload | <span class="nb">base64</span> <span class="nt">-d</span> | jq .predicate</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="verification-of-the-attestation">Verification of the attestation</h3>
<div class="paragraph">
<p>And lastly in the same manner as before the attestation can also be verified by the means of
cosign:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
    ghcr.io/sigstore/cosign/cosign:v2.4.1 <span class="se">\</span>
    verify-attestation  <span class="se">\</span>
        <span class="nt">--type</span> cyclonedx <span class="se">\</span>
        <span class="nt">--certificate-oidc-issuer</span><span class="o">=</span>https://github.com/login/oauth <span class="se">\</span>
        <span class="nt">--certificate-identity</span><span class="o">=</span>christoph@unexist.dev <span class="se">\</span>
        <span class="nv">$DIGEST</span> | jq <span class="s2">".[] | .critical"</span>
podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
    ghcr.io/sigstore/cosign/cosign:v2.4.1 <span class="se">\</span>
    verify-attestation  <span class="se">\</span>
        <span class="nt">--type</span> cyclonedx <span class="se">\ </span><i class="conum" data-value="1"></i><b>(1)</b>
        <span class="nt">--certificate-oidc-issuer</span><span class="o">=</span>https://github.com/login/oauth <span class="se">\</span>
        <span class="nt">--certificate-identity</span><span class="o">=</span>christoph@unexist.dev <span class="se">\</span>
        <span class="nv">$DIGEST</span> <span class="o">&gt;</span> /dev/null <i class="conum" data-value="2"></i><b>(2)</b>

Verification <span class="k">for </span>localhost:5000/todo-service@sha256:5c6bb144aaed7d3e4eb58ac6bcdbf2a68d0409d5328f81c9d413e9301e2517a9 <span class="nt">--</span>
The following checks were performed on each of these signatures:
  - The cosign claims were validated
  - Existence of the claims <span class="k">in </span>the transparency log was verified offline
  - The code-signing certificate was verified using trusted certificate authority certificates
Certificate subject: christoph@unexist.dev
Certificate issuer URL: https://github.com/login/oauth</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Here we pass some expectations to the checks.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>We don&#8217;t want to see the exact same content from the previous step again.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Passing bogus information or trying to verify the wrong digest leads to an error:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell">podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/workspace <span class="nt">--network</span><span class="o">=</span>host <span class="se">\</span>
    ghcr.io/sigstore/cosign/cosign:v2.4.1 <span class="se">\</span>
    verify-attestation  <span class="se">\</span>
        <span class="nt">--type</span> cyclonedx <span class="se">\</span>
        <span class="nt">--certificate-oidc-issuer</span><span class="o">=</span>https://github.com/login/oauth <span class="se">\</span>
        <span class="nt">--certificate-identity</span><span class="o">=</span>anon@anon.rs <span class="se">\</span>
        <span class="nv">$DIGEST</span> <span class="o">&gt;</span> /dev/null
Error: no matching attestations: none of the expected identities matched what was <span class="k">in </span>the certificate, got subjects <span class="o">[</span>christoph@unexist.dev] with issuer https://github.com/login/oauth
main.go:74: error during <span class="nb">command </span>execution: no matching attestations: none of the expected identities matched what was <span class="k">in </span>the certificate, got subjects <span class="o">[</span>christoph@unexist.dev] with issuer https://github.com/login/oauth</code></pre>
</div>
</div>
<div class="paragraph">
<p>Phew that was quite lengthy to reach this point, time for a small recap.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>During the course of this post we have seen how OCI-registries can be leveraged to store almost
any kind of artifact.
The layered structure and format allows to add additional metadata and ancillary artifacts like
Helm-charts can be put there to rest as well.</p>
</div>
<div class="paragraph">
<p>Bill of materials allow quick scan of layers for known vulnerabilities and combined with proper
signing can the security of the supply chain be further strengthened.
Alas this is also no <a href="https://en.wikipedia.org/wiki/No_Silver_Bullet">silver bullet</a> and takes lots of work to get it right in
automatic workflows.</p>
</div>
<div class="paragraph">
<p>I personally think this is a great addition, solves my initial hunt for artifact storage and also
eases the handling of all the dependencies of different kind of artifacts in a more secure way.
Next stop for me is to compile all this into a shiny new
<a href="https://unexist.blog/documentation/myself/2024/10/25/decision-records.html">Architecture Decision Record</a>.
and discuss is with my team.</p>
</div>
<div class="paragraph">
<p>All examples can be found here hidden in the <a href="https://taskfile.dev/">taskfiles</a>:</p>
</div>
<div class="paragraph">
<p><a href="https://github.com/unexist/showcase-oci-registries" class="bare">https://github.com/unexist/showcase-oci-registries</a></p>
</div>
</div>
</div>
<div id="footnotes">
<hr>
<div class="footnote" id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. At least to me
</div>
<div class="footnote" id="_footnotedef_2">
<a href="#_footnoteref_2">2</a>. Really the last one for the course of this post..
</div>
<div class="footnote" id="_footnotedef_3">
<a href="#_footnoteref_3">3</a>. Maybe I just like their mascots?
</div>
<div class="footnote" id="_footnotedef_4">
<a href="#_footnoteref_4">4</a>. I hope we are not - anyway!
</div>
<div class="footnote" id="_footnotedef_5">
<a href="#_footnoteref_5">5</a>. I&#8217;ve got plenty of apps for that..
</div>
</div>]]></content><author><name>Christoph Kappel</name></author><category term="tech" /><category term="container" /><category term="oci" /><category term="sbom" /><category term="oras" /><category term="cosign" /><category term="trivy" /><category term="grype" /><category term="syft" /><category term="cyclonedx" /><category term="showcase" /><summary type="html"><![CDATA[This blog post demonstrates how ORAS and cosign can be used to leverage OCI-compliant registries to handle all kind of artifacts in a more secure way.]]></summary></entry><entry><title type="html">Domain storytelling</title><link href="https://unexist.blog/communication/2025/04/29/domain-storytelling.html" rel="alternate" type="text/html" title="Domain storytelling" /><published>2025-04-29T16:33:00+02:00</published><updated>2025-04-29T16:33:00+02:00</updated><id>https://unexist.blog/communication/2025/04/29/domain-storytelling</id><content type="html" xml:base="https://unexist.blog/communication/2025/04/29/domain-storytelling.html"><![CDATA[<div class="quoteblock">
<blockquote>
The great enemy of communication is the illusion of it.
</blockquote>
<div class="attribution">
&#8212; William H. Whyte
</div>
</div>
<div class="paragraph">
<p>I think we can all agree on communication is hard and especially when you want to convey something
that is perfectly clear to you.
One simple explanation can be the <a href="https://en.wikipedia.org/wiki/Curse_of_knowledge">curse of knowledge</a>, but this doesn&#8217;t help me (at least) on my
next struggle to find the right words without getting frustrated first.</p>
</div>
<div class="paragraph">
<p>This kind of struggle can be mildly said interesting in most cases during personal communication,
but what happens during anything related to business, like complex requirement of your next big
product?</p>
</div>
<div class="paragraph">
<p>During the course of this post I want to put emphasis on visual communication, which can help to
support any narrativ and ultimately provide additional help in getting understood.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Like many posts in this blog before, we again use my sample todo application - if you still
haven&#8217;t seen it yet you can find an <a href="https://www.openapis.org/">OpenAPI</a> specification here:<br>
<a href="https://blog.unexist.dev/redoc/#tag/Todo" class="bare">https://blog.unexist.dev/redoc/#tag/Todo</a>
</td>
</tr>
</table>
</div>
<div class="sect1">
<h2 id="simple-use-cases">Simple use-cases</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Even with business requirements it is possible to start simple and one of the simplest things a
user probably wants to do with our application is following:</p>
</div>
<div class="sidebarblock">
<div class="content">
<div class="paragraph">
<p>A user wants to create a new todo entry.</p>
</div>
</div>
</div>
<div class="paragraph">
<p>Simple enough and perfectly straight forward, but the same can be expressed (and supported not
replaced mind you) with a simple use-case diagram:</p>
</div>
<p><object data='/uml/984289746c00f0f66a22c101c0b96e96.svg' type='image/svg+xml'  class='plantuml'></object></p>
<div class="paragraph">
<p>I suppose if I&#8217;d ask you for your first thoughts on this example now I&#8217;d probably get something in
the  range of this just adds clutter and is completely overkill for this really simple matter.</p>
</div>
<div class="paragraph">
<p>So still why do I insist this adds benefits?</p>
</div>
<div class="sidebarblock">
<div class="content">
<div class="title">Excursion: Visual perception</div>
<div class="paragraph">
<p>We humans are <a href="https://ifvp.org/content/why-our-brain-loves-pictures">really good in visual perception</a> and a lot of information is gathered that way in
literally a glimpse.</p>
</div>
<div class="paragraph">
<p>You can easily verify it on your own:
How long does it take to read the single requirement vs how long do you have to look at the picture?</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
There are so many sources to cite from, if you are curious about this whole topic please
give <a href="https://scholar.google.com/scholar?hl=en&amp;as_sdt=0%2C5&amp;q=visual+perception&amp;btnG=&amp;oq=visual+perc">Google Scholar</a> a spin, but going further probably leads away from the point I want to make.
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="technical-use-cases">Technical use-cases</h3>
<div class="paragraph">
<p>Targeting the right audience is also key here, but still adding too much technical jargon and
information to a use-case kind of beats the benefit of getting everything in a quick glance.</p>
</div>
<div class="paragraph">
<p><a href="https://en.wikipedia.org/wiki/Unified_Modeling_Language">UML</a> might offer many niceties, but please ask yourself does the extension of the previous
use-case add anything of value?</p>
</div>
<p><object data='/uml/42bb42a16bc0f05ba1c993059e339677.svg' type='image/svg+xml'  class='plantuml'></object></p>
<div class="paragraph">
<p>Let&#8217;s move on to a more complex use-case.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="advanced-use-cases">Advanced use-cases</h2>
<div class="sectionbody">
<div class="paragraph">
<p>No creative hat on today, so I am just going to re-use an idea from my previous [logging/tracing
showcase][]:</p>
</div>
<div class="sidebarblock">
<div class="content">
<div class="paragraph">
<p>An admin wants to define a list of swear words.<br>
A user wants to create a new todo entry free of swearwords.</p>
</div>
</div>
</div>
<div class="paragraph">
<p>This just adds a bit more complexity, but with focus on the business side the updated use-case
can look like this:</p>
</div>
<p><object data='/uml/0f3b5ba4c8d32c245e679ebeb0af093a.svg' type='image/svg+xml'  class='plantuml'></object></p>
<div class="paragraph">
<p>So far this probably doesn&#8217;t bring any real benefit business-wise, so let us quickly add a way to
actual see the created todo entries and awestruck our competitors:</p>
</div>
<p><object data='/uml/da28d7ee7a3c1c52bfc27e796686c6d7.svg' type='image/svg+xml'  class='plantuml'></object></p>
<div class="paragraph">
<p>There are many more ways to improve these use-cases and I don&#8217;t lack funny ideas, but the main goal
here was to demonstrate the power of visual use-cases and the story that can unfold.</p>
</div>
<div class="paragraph">
<p>Instead of creating all of these use-cases in isolation, we can also carry on with the story
idea and actually tell them.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="domain-stories">Domain-stories</h2>
<div class="sectionbody">
<div class="paragraph">
<p>At its heart <a href="https://domainstorytelling.org">Domain Storytelling</a> is a workshop format, usually held by a domain expert and a
supporting moderator, who share examples how they actually work inside the domain.</p>
</div>
<div class="paragraph">
<p>While the expert explains the domain, the moderator tries to record the story with a simple
pictographic language.
Each domain story covers one concrete example and can be directly used to verify if the story has
been understood correctly or otherwise adjusted.</p>
</div>
<div class="paragraph">
<p>This approach allows all participants to learn the domain language (see <a href="https://martinfowler.com/bliki/UbiquitousLanguage.html">ubiquitous language</a>),
get an understanding of the activities of the domain and also discover boundaries between
the different parts (see <a href="https://martinfowler.com/bliki/BoundedContext.html">bounded contexts</a>).</p>
</div>
<div class="sect2">
<h3 id="show-and-tell">Show and tell</h3>
<div class="paragraph">
<p>The authors of the book <a href="https://domainstorytelling.org">Domain Storytelling</a> <a href="#domstory">[domstory]</a> also provided <a href="https://egon.io/">Egon</a>, a lightweight
editor to support the workshop format.</p>
</div>
<div class="paragraph">
<p>One of my personal favorite features among others it the replay button to actually blend in the
different steps like in a good slidedeck.</p>
</div>
<div class="paragraph">
<p>If we translate our last use-case to a simple domain story, one version could be like this:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/domain_storytelling/todo.egn.svg" alt="Generated with Egon">
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Writing and evaluating requirements can be a progressive approach as we have seen with the
evolution from a single no-brainer requirement to a more complex one.
Going even further, the whole process can be done in a conversational and story-telling way and
directly improve the understanding of all participants.</p>
</div>
<div class="paragraph">
<p>Using diagrams for communication isn&#8217;t something new, still I rarely see developers using them.
I sometimes think this might be a problem of tooling, but with the rise of
<a href="https://docsascode.org/">documentation-as-code</a> this shouldn&#8217;t be an excuse anymore.</p>
</div>
<div class="paragraph">
<p>Domain storytelling is a different approach to the whole idea and even if you don&#8217;t follow this
approach by detail, your projects can still benefit from the way Egon tells your stories.</p>
</div>
<div class="paragraph">
<p>If you interested in this topic and want to read more about it I highly suggest to have a look at
these two books:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Domain Storytelling <a href="#domstory">[domstory]</a></p>
</li>
<li>
<p><a href="https://communicationpatternsbook.com/">Communication Patterns</a> <a href="#viscom">[viscom]</a></p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="bibliography">Bibliography</h2>
<div class="sectionbody">
<div class="ulist bibliography">
<ul class="bibliography">
<li>
<p><a id="domstory"></a>[domstory] Stefan Hofer, Henning Schwentner, Domain Storytelling: A Collaborative, Visual and Agile Way to Build Domain-Driven Software, Addison-Wesley 2021</p>
</li>
<li>
<p><a id="viscom"></a>[viscom] Jacqui Read, Communication Patterns: A Guide for Developers and Architects, O&#8217;Reilly 2023</p>
</li>
</ul>
</div>
</div>
</div>]]></content><author><name>Christoph Kappel</name></author><category term="communication" /><category term="ddd" /><category term="diagrams" /><category term="use-case" /><category term="storytelling" /><category term="uml" /><summary type="html"><![CDATA[This blog post introduces domain storytelling as an easy and interactive way of conveying use-cases with strong ties to domain-driven design.]]></summary></entry><entry><title type="html">OpenAPI and AsciiDoc in the Mix</title><link href="https://unexist.blog/documentation/myself/2025/04/07/openapi-and-asciidoc-in-the-mix.html" rel="alternate" type="text/html" title="OpenAPI and AsciiDoc in the Mix" /><published>2025-04-07T18:31:00+02:00</published><updated>2025-04-07T18:31:00+02:00</updated><id>https://unexist.blog/documentation/myself/2025/04/07/openapi-and-asciidoc-in-the-mix</id><content type="html" xml:base="https://unexist.blog/documentation/myself/2025/04/07/openapi-and-asciidoc-in-the-mix.html"><![CDATA[<div class="paragraph">
<p>I am getting more and more obsessed with centralized documentation and this isn&#8217;t because I enjoy
writing documentation (which I unfortunately really do), but more due sheer lack of it in my day
job and all the related issues we are currently facing.</p>
</div>
<div class="paragraph">
<p>Pushing ideas like the one from my previous post
(<a href="https://unexist.blog/documentation/myself/2024/12/26/bringing-documentation-together.html">Bringing documentation together</a>)
certainly helps to make writing docs easier, but there are still some loose ends to follow -
like API.</p>
</div>
<div class="paragraph">
<p>So this post is going to demonstrate how <a href="https://swagger.io/specification/">OpenAPI</a> (or formerly <a href="https://swagger.io/">Swagger</a>) can be converted
into shiny <a href="https://asciidoc.org/">AsciiDoc</a> and be brought into the mix.</p>
</div>
<div class="sect1">
<h2 id="why-openapi">Why OpenAPI?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>There are many ways to document API (mind you any documentation is better than none!), but keeping
established standards like <a href="https://swagger.io/specification/">OpenAPI</a> and <a href="https://www.asyncapi.com/en">AsyncAPI</a> (which isn&#8217;t to far off) really help to
keep the cognitive churn low while trying to understand what a document is trying to convey.</p>
</div>
<div class="paragraph">
<p>And from a developer&#8217;s perspective there are many low-hanging fruits:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Code-first or API-first - you decide</p>
</li>
<li>
<p>Many generators in both directions available - like <a href="https://github.com/SMILEY4/ktor-openapi-tools">ktor-openapi-tools</a> used in the example</p>
</li>
<li>
<p>Tools like <a href="https://swagger.io/tools/swagger-ui/">Swagger UI</a> and <a href="https://github.com/Redocly/redoc">Redoc</a></p>
</li>
<li>
<p>Comes pre-assembled with a testing tool</p>
</li>
</ul>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
Still not sold? <a href="https://swagger.io/blog/api-strategy/benefits-of-openapi-api-development/" class="bare">https://swagger.io/blog/api-strategy/benefits-of-openapi-api-development/</a>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="converting-to-asciidoc">Converting to AsciiDoc</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Again, there are dozens of options to select from..
Since I rely on the <a href="https://github.com/confluence-publisher/confluence-publisher">confluence publisher plugin</a> my initial pick was something with
<a href="https://maven.apache.org/">Maven</a>-integration as well, but unfortunately <a href="https://github.com/joensson/swagger2asciidoc">swagger2asciidoc</a> has been unmaintained for
quite some time.
I actually tried to use it, but this was more like an educative endeavor for learning what happens
to neglected packages.</p>
</div>
<div class="paragraph">
<p>The next best option and probably should have been my first pick anyway is OpenAPI with its
exhaustive list of generators.
They offer a plethora of different ways to convert specs and thankfully
<a href="https://openapi-generator.tech/docs/generators/asciidoc">AsciiDoc is among them</a>.</p>
</div>
<div class="paragraph">
<p>If we omit all nitty-gritty details it boils down to this call:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>openapi-generator-cli generate <span class="nt">-g</span> asciidoc <span class="se">\</span>
    <span class="nt">--skip-validate-spec</span> <span class="se">\ </span><i class="conum" data-value="1"></i><b>(1)</b>
    <span class="nt">--input-spec</span><span class="o">=</span>src/site/asciidoc/spec/openapi.json <span class="se">\</span>
    <span class="nt">--output</span><span class="o">=</span>src/site/asciidoc <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Let us ignore version handling and maturity of my own spec for now</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>This is my preferred structure for Maven-based documentations</td>
</tr>
</table>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
This can also be run from a container, see either <a href="https://hub.docker.com/r/openapitools/openapi-generator-cli">openapi-generator-cli</a> or have a look
at my <a href="https://github.com/unexist/showcase-documentation-openapi-asciidoc/blob/master/infrastructure/Containerfile">containerfile</a> for even more dependencies.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>When everything works well a resulting document like this can be viewed:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/openapi_and_asciidoc_in_the_mix/asciidoc.png" alt="asciidoc">
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="customizing-the-document">Customizing the document</h2>
<div class="sectionbody">
<div class="paragraph">
<p>One of the strong points of AsciiDoc is surely its extensibility and this also true for the
generator pipeline we are using now.</p>
</div>
<div class="paragraph">
<p>Per default, the generator offers a lot of different entrypoints to provide custom content for
inclusion in the final document, without doing fancy hacks like e.g. an include of the generated
document in your own one.</p>
</div>
<div class="paragraph">
<p>If you have a closer look at the actual generated document you can see lots of commented out
includes like:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="adoc">[abstract]
.Abstract
Simple todo service


// markup not found, no include::{specDir}intro.adoc[opts=optional]</code></pre>
</div>
</div>
<div class="paragraph">
<p>An introduction sounds like a good idea, so we could use the space there to inform our readers
about the automatic updates of the document:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span><span class="nb">cat </span>asciidoc/src/site/asciidoc/spec/intro.adoc
<span class="o">[</span>CAUTION]
This page is updated automatically, please <span class="k">do</span> <span class="k">*</span>not<span class="k">*</span> edit manually.</code></pre>
</div>
</div>
<div class="paragraph">
<p>After that we have to tell the generator to actually include our document.
When started, it is looking for these
<code>templates</code>.{fn-templates}.<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup>
inside the <code>specDir</code>, something we haven&#8217;t set before, but we are quite able to do.</p>
</div>
<div class="paragraph">
<p>This only requires a minor change of our previous commandline:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>openapi-generator-cli generate <span class="nt">-g</span> asciidoc <span class="se">\</span>
    <span class="nt">--skip-validate-spec</span> <span class="se">\ </span><i class="conum" data-value="1"></i><b>(1)</b>
    <span class="nt">--input-spec</span><span class="o">=</span>src/site/asciidoc/spec/openapi.json <span class="se">\</span>
    <span class="nt">--output</span><span class="o">=</span>src/site/asciidoc <span class="se">\</span>
    <span class="nt">--additional-properties</span><span class="o">=</span><span class="nv">specDir</span><span class="o">=</span>spec/,useIntroduction<span class="o">=</span><span class="nb">true</span> <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td><a href="https://openapi-generator.tech/docs/usage/#generate">Additional properties</a> can be used to pass down configuration directly to the AsciiDoc renderer</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>And hopefully, a run of the above rewards with an output like this:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/openapi_and_asciidoc_in_the_mix/introduction.png" alt="introduction">
</div>
</div>
<div class="paragraph">
<p>There are many more templates that can be filled and I would gladly supply a list, but at the time
of writing I just can offer to grep the document on your own:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span><span class="se">\g</span>rep <span class="nt">-m</span> 5 <span class="s2">"// markup not found"</span> src/site/asciidoc/index.adoc
// markup not found, no include::<span class="o">{</span>specDir<span class="o">}</span>todo/POST/spec.adoc[opts<span class="o">=</span>optional]
// markup not found, no include::<span class="o">{</span>snippetDir<span class="o">}</span>todo/POST/http-request.adoc[opts<span class="o">=</span>optional] <i class="conum" data-value="1"></i><b>(1)</b>
// markup not found, no include::<span class="o">{</span>snippetDir<span class="o">}</span>todo/POST/http-response.adoc[opts<span class="o">=</span>optional]
// markup not found, no include::<span class="o">{</span>specDir<span class="o">}</span>todo/POST/implementation.adoc[opts<span class="o">=</span>optional]
// markup not found, no include::<span class="o">{</span>specDir<span class="o">}</span>todo/<span class="se">\{</span><span class="nb">id</span><span class="se">\}</span>/DELETE/spec.adoc[opts<span class="o">=</span>optional]</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Looks like we can also supply snippets to the example sections - neat!</td>
</tr>
</table>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
<div class="paragraph">
<p>During my tests I stumbled upon a weird behavior, whereas there are different checks per index
and generation phase, which have different requirements to the actual path.</p>
</div>
<div class="paragraph">
<p>This made it necessary for me to fix this with a symlink in my builds:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> .:/openapi <span class="nt">-it</span> docker.io/unexist/openapi-builder:0.3 <span class="se">\</span>
  sh <span class="nt">-c</span> <span class="s2">"cd /openapi </span><span class="se">\</span><span class="s2">
    &amp;&amp; ln -s asciidoc/src/site/asciidoc/spec spec </span><span class="se">\ </span><span class="s2"><i class="conum" data-value="1"></i><b>(1)</b>
    &amp;&amp; openapi-generator-cli generate -g asciidoc </span><span class="se">\</span><span class="s2">
        --skip-validate-spec </span><span class="se">\</span><span class="s2">
        --input-spec=asciidoc/src/site/asciidoc/spec/openapi.json </span><span class="se">\</span><span class="s2">
        --output=asciidoc/src/site/asciidoc </span><span class="se">\</span><span class="s2">
        --additional-properties=specDir=spec/,useIntroduction=true' </span><span class="se">\</span><span class="s2">
    &amp;&amp; unlink spec"</span> <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Nasty I know, but maybe I got a response to this when you actually read this post:
<a href="https://github.com/OpenAPITools/openapi-generator/issues/20996" class="bare">https://github.com/OpenAPITools/openapi-generator/issues/20996</a></td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Just tidy up afterwards or rather skip altogether in a container</td>
</tr>
</table>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="publish-the-document">Publish the document</h2>
<div class="sectionbody">
<div class="paragraph">
<p>I think this is the third time I tease how everything can be pushed to <a href="https://www.atlassian.com/software/confluence">Confluence</a>, but since
I don&#8217;t run any personal instance just feel teased again:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>mvn <span class="nt">-f</span> pom.xml <span class="se">\</span>
    <span class="nt">-DCONFLUENCE_URL</span><span class="o">=</span><span class="s2">"unexist.blog"</span> <span class="se">\</span>
    <span class="nt">-DCONFLUENCE_SPACE_KEY</span><span class="o">=</span><span class="s2">"UXT"</span> <span class="se">\</span>
    <span class="nt">-DCONFLUENCE_ANCESTOR_ID</span><span class="o">=</span><span class="s2">"123"</span> <span class="se">\</span>
    <span class="nt">-DCONFLUENCE_USER</span><span class="o">=</span><span class="s2">"unexist"</span> <span class="se">\</span>
    <span class="nt">-DCONFLUENCE_TOKEN</span><span class="o">=</span><span class="s2">"secret123"</span> <span class="se">\</span>
    <span class="nt">-P</span> generate-docs-and-publish generate-resources</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>What have we done here?
Strictly speaking this doesn&#8217;t bring many advantages, especially when the tooling for OpenAPI
looks so polished like this:</p>
</div>
<div class="paragraph">
<p><a href="https://unexist.blog/redoc" class="bare">https://unexist.blog/redoc</a></p>
</div>
<div class="paragraph">
<p>The ultimate goal of this is to create a central place where these specifications can be stored,
without too many hurdles for non-dev stakeholders.
Developers do well, when told the specs can be generated via
<a href="https://www.gnu.org/software/make/manual/make.html">Makefile</a>.<sup class="footnote">[<a id="_footnoteref_2" class="footnote" href="#_footnotedef_2" title="View footnote.">2</a>]</sup>, but what about other roles like e.g.
testers?</p>
</div>
<div class="paragraph">
<p>Back then we rolled a special infrastructure container, which basically included SwaggerUI along
with the current versions of our specs, but infrastructure <strong>is</strong> additional work that has to be
done <strong>and</strong> everything that leads to it must be maintained.</p>
</div>
<div class="paragraph">
<p>Whatever you do, proving easy access to documentation really helps to reach a common understanding
and also might help to keep it up-to-date.</p>
</div>
<div class="paragraph">
<p>All examples can be found here:</p>
</div>
<div class="paragraph">
<p><a href="https://github.com/unexist/showcase-documentation-openapi-asciidoc" class="bare">https://github.com/unexist/showcase-documentation-openapi-asciidoc</a></p>
</div>
</div>
</div>
<div id="footnotes">
<hr>
<div class="footnote" id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. This might be misleading due to the integration of <a href="https://mustache.github.io/">Mustache</a>, but what are they actually called?
</div>
<div class="footnote" id="_footnotedef_2">
<a href="#_footnoteref_2">2</a>. Or even better via <a href="https://taskfile.dev/">Taskfile</a>!
</div>
</div>]]></content><author><name>Christoph Kappel</name></author><category term="documentation" /><category term="asciidoc" /><category term="openapi" /><category term="asciidoxy" /><category term="kotlin" /><category term="showcase" /><summary type="html"><![CDATA[This blog post rounds up on converting OpenAPI specs to AsciiDoc and how this can be brought into the mix of centralized documentation.]]></summary></entry><entry><title type="html">Gitlab with Podman</title><link href="https://unexist.blog/cicd/2025/02/15/gitlab-with-podman.html" rel="alternate" type="text/html" title="Gitlab with Podman" /><published>2025-02-15T18:07:00+01:00</published><updated>2025-02-15T18:07:00+01:00</updated><id>https://unexist.blog/cicd/2025/02/15/gitlab-with-podman</id><content type="html" xml:base="https://unexist.blog/cicd/2025/02/15/gitlab-with-podman.html"><![CDATA[<div class="sect1">
<h2 id="gitlab-with-podman">Gitlab with Podman</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The ultimate goal for my previous post about <a href="https://dagger.io/">Dagger</a> was to demonstrate the combination of it
with <a href="https://about.gitlab.com/">Gitlab</a> and <a href="https://podman.io/">Podman</a>, but unfortunately I ran into so many different problems I made the
decision to break it apart.</p>
</div>
<div class="paragraph">
<p>This is second part of a small series and explains how to set up Gitlab with Podman-in-Podman
and various pitfalls along the way.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
If you are looking for the first part just follow this link over here:<br>
<a href="https://unexist.blog/cicd/2024/05/05/building-with-dagger.html">Building with Dagger</a>.
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="preparations">Preparations</h3>
<div class="paragraph">
<p>The first step in order to start Gitlab is to provide an SSL cert, but to make this a lot more
interesting we rely on a self-signed one:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>openssl req <span class="nt">-newkey</span> rsa:4096 <span class="nt">-x509</span> <span class="nt">-sha512</span> <span class="nt">-days</span> 365 <span class="nt">-nodes</span> <span class="se">\</span>
    <span class="nt">-out</span> gitlab.crt <span class="nt">-keyout</span> gitlab.key <span class="se">\</span>
    <span class="nt">-addext</span> <span class="s2">"subjectAltName=DNS:gitlab"</span> <span class="se">\ </span><i class="conum" data-value="1"></i><b>(1)</b>
    <span class="nt">-subj</span> <span class="s2">"/C=DE/ST=DE/L=DE/O=unexist.dev/OU=showcase/CN=gitlab"</span> <i class="conum" data-value="2"></i><b>(2)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This line is essential, otherwise Gitlab won&#8217;t accept this cert</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>We are going to use <strong>gitlab</strong> for the hostname, so make sure to add it to your hosts file</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Next up is the actual config of Gitlab.</p>
</div>
<div class="paragraph">
<p>There is aplenty that can actually be configured beforehand, especially in
<a href="https://docs.gitlab.com/omnibus/settings/memory_constrained_envs.html">memory constrained environments</a> it is beneficial to disable services like <a href="https://prometheus.io/">Prometheus</a>, but
here we trust in <a href="https://en.wikipedia.org/wiki/Convention_over_configuration">convention over configuration</a> and just include only the bare minimum to
run Gitlab:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="ruby"><span class="n">external_url</span> <span class="s1">'https://gitlab:10443/'</span>
<span class="n">registry_external_url</span> <span class="s1">'https://gitlab:4567'</span> <i class="conum" data-value="1"></i><b>(1)</b>

<span class="n">registry_nginx</span><span class="p">[</span><span class="s1">'enable'</span><span class="p">]</span> <span class="o">=</span> <span class="kp">true</span>
<span class="n">registry_nginx</span><span class="p">[</span><span class="s1">'listen_port'</span><span class="p">]</span> <span class="o">=</span> <span class="mi">4567</span> <i class="conum" data-value="2"></i><b>(2)</b>

<span class="n">nginx</span><span class="p">[</span><span class="s1">'ssl_certificate'</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"/etc/gitlab/ssl/gitlab.crt"</span>
<span class="n">nginx</span><span class="p">[</span><span class="s1">'ssl_certificate_key'</span><span class="p">]</span> <span class="o">=</span> <span class="s2">"/etc/gitlab/ssl/gitlab.key"</span>
<span class="n">nginx</span><span class="p">[</span><span class="s1">'listen_port'</span><span class="p">]</span> <span class="o">=</span> <span class="mi">10443</span> <i class="conum" data-value="3"></i><b>(3)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Setting the ports here causes problems elsewhere, so better also set the ports in &lt;2&gt; and &lt;3&gt;</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>My initial idea was to use the registry as a cache, but more to that later</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Nginx usually picks the port from <em>external_url</em>, which is not what we want to do</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Like <a href="https://kubernetes.io/">Kubernetes</a>, Podman allows to group or rather encapsulate containers in <a href="https://developers.redhat.com/blog/2019/01/15/podman-managing-containers-pods">pods</a> and also to
convert them afterward, so let us quickly create one:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman pod create <span class="nt">-n</span> showcase <span class="nt">--network</span> bridge  <span class="se">\</span>
        <span class="nt">-p</span> 10022:22 <span class="sb">`</span><span class="c"># Gitlab ssh` \</span>
        <span class="nt">-p</span> 10443:10443 <span class="sb">`</span><span class="c"># Gitlab web` \</span>
        <span class="nt">-p</span> 4567:4567 <span class="sb">`</span><span class="c"># Gitlab registry`</span>
e91d11fdeb168c5713c9f48a50ab736db59d88ae7e39b807371923dcf4f26199</code></pre>
</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
This can be done with the make target <code>pd-pod-create</code>.
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="starting-gitlab">Starting Gitlab</h3>
<div class="paragraph">
<p>Once everything is in place we can fire up Gitlab:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">-dit</span> <span class="nt">--name</span> gitlab <span class="nt">--pod</span><span class="o">=</span>gitlab <span class="se">\</span>
    <span class="nt">--memory</span><span class="o">=</span>4096m <span class="nt">--cpus</span><span class="o">=</span>4 <span class="se">\</span>
    <span class="nt">-v</span> ./gitlab.crt:/etc/gitlab/ssl/gitlab.crt <span class="se">\ </span><i class="conum" data-value="1"></i><b>(1)</b>
    <span class="nt">-v</span> ./gitlab.key:/etc/gitlab/ssl/gitlab.key <span class="se">\</span>
    <span class="nt">-v</span> ./gitlab.rb:/etc/gitlab/gitlab.rb <span class="se">\ </span><i class="conum" data-value="2"></i><b>(2)</b>
    <span class="nt">-v</span> ./gitlab-data:/var/opt/gitlab <span class="se">\</span>
    <span class="nt">-e</span> <span class="nv">GITLAB_ROOT_PASSWORD</span><span class="o">=</span>YourPassword <span class="se">\ </span><i class="conum" data-value="3"></i><b>(3)</b>
    docker.io/gitlab/gitlab-ce:latest
17349b87f81aa9eb7230f414923cf491c84a36a87d61057f8dc2f8f82c7ea60a</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>We pass our new certs via volume mounts to Gitlab</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Our previously modified minimal config</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Let&#8217;s be creative</td>
</tr>
</table>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
This can also be done with the make target <code>pd-gitlab</code>.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Once the container is running Gitlab can be reached at following address:
<a href="https://localhost:10443" class="bare">https://localhost:10443</a></p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/gitlab_with_podman/gitlab_login.png" alt="gitlab login">
</div>
<div class="title">Screenshot of the login screen of Gitlab</div>
</div>
<div class="paragraph">
<p>Great success, but unfortunately Gitlab alone is only half the deal.</p>
</div>
</div>
<div class="sect2">
<h3 id="adding-a-runner">Adding a runner</h3>
<div class="paragraph">
<p>Setting up a runner which is able to spawn new containers inside Podman is a bit tricky and
requires to build a specially configured container first.</p>
</div>
<div class="paragraph">
<p>Luckily for us other <a href="https://opensource.com/article/23/3/podman-gitlab-runners">people struggled</a> with the same idea and did the heavy lifting
for us:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman build <span class="nt">-t</span> <span class="si">$(</span>RUNNER_IMAGE_NAME<span class="si">)</span> <span class="nt">-f</span> runner/Containerfile <span class="se">\ </span><i class="conum" data-value="1"></i><b>(1)</b>
    <span class="nt">--build-arg</span><span class="o">=</span><span class="nv">GITLAB_URL</span><span class="o">=</span><span class="si">$(</span>GITLAB_URL<span class="si">)</span> <span class="se">\ </span><i class="conum" data-value="2"></i><b>(2)</b>
    <span class="nt">--build-arg</span><span class="o">=</span><span class="nv">REGISTRY_URL</span><span class="o">=</span><span class="si">$(</span>REGISTRY_URL<span class="si">)</span> <span class="se">\</span>
    <span class="nt">--build-arg</span><span class="o">=</span><span class="nv">PODNAME</span><span class="o">=</span><span class="si">$(</span>PODNAME<span class="si">)</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This relies on the <a href="https://registry.gitlab.com/qontainers/pipglr">pipglr</a> project</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>This is an excerpt from the provided Makefile, so please consider the env variables properly set</td>
</tr>
</table>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
This can also be done with the make target <code>pd-runner-podman-build</code>.
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="registration-of-the-runner">Registration of the runner</h3>
<div class="paragraph">
<p>The current registration process requires us to register a new runner inside Gitlab first and
this can be done via <span class="menuseq"><b class="menu">Admin</b>&#160;<i class="fa fa-angle-right caret"></i> <b class="submenu">CICD</b>&#160;<i class="fa fa-angle-right caret"></i> <b class="submenu">Runners</b>&#160;<i class="fa fa-angle-right caret"></i> <b class="menuitem">New instance runner</b></span> at:
<a href="https://localhost:10443/admin/runners" class="bare">https://localhost:10443/admin/runners</a></p>
</div>
<div class="paragraph">
<p>Once submitted the redirection is going to fail, since our host machine doesn&#8217;t know the hostname
<code>gitlab</code>:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/gitlab_with_podman/gitlab_register_localhost.png" alt="gitlab register localhost">
</div>
<div class="title">Screenshot of the wrong address</div>
</div>
<div class="paragraph">
<p>This can be bypassed by just replacing <code>gitlab</code> with <code>localhost</code> or with a quick edit of the
hosts file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span><span class="nb">grep </span>127 /etc/hosts
127.0.0.1     localhost
127.0.0.1     meanas
127.0.0.1     gitlab</code></pre>
</div>
</div>
<div class="paragraph">
<p>Registration of the actual runner is bit a more involved, but remember the other people?
pipglr, the actual hero our story, comes prepared and brings some <a href="https://docs.docker.com/engine/manage-resources/labels/">container labels</a> to execute
the registration commands.</p>
</div>
<div class="paragraph">
<p>I took the liberty to throw everything into a Makefile target, and we just call it directly this
time:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ TOKEN</span><span class="o">=</span>glrt-t1_QnEnk-yx3sdgVT-DYt7i make pd-runner-podman
<span class="c"># This requires Podman &gt;=4.1 </span><i class="conum" data-value="1"></i><b>(1)</b>
<span class="c">#podman secret exists REGISTRATION_TOKEN &amp;&amp; podman secret rm REGISTRATION_TOKEN || true</span>
<span class="c">#podman secret exists config.toml &amp;&amp; podman secret rm config.toml || true</span>
Error: no secret with name or <span class="nb">id</span> <span class="s2">"REGISTRATION_TOKEN"</span>: no such secret
Error: no secret with name or <span class="nb">id</span> <span class="s2">"config.toml"</span>: no such secret
1a02dae2a667dbddbdc8bd7b0
Runtime platform                                    <span class="nb">arch</span><span class="o">=</span>amd64 <span class="nv">os</span><span class="o">=</span>linux <span class="nv">pid</span><span class="o">=</span>1 <span class="nv">revision</span><span class="o">=</span>690ce25c <span class="nv">version</span><span class="o">=</span>17.8.3
Running <span class="k">in </span>system-mode.

Created missing unique system ID                    <span class="nv">system_id</span><span class="o">=</span>s_d3cc561989f6
Verifying runner... is valid                        <span class="nv">runner</span><span class="o">=</span>t1_QnEnk-
Runner registered successfully. Feel free to start it, but <span class="k">if </span>it<span class="s1">'s running already the config should be automatically reloaded!

Configuration (with the authentication token) was saved in "/etc/gitlab-runner/config.toml"
# Fix SSL config to contact Gitlab registry
db86c90b8d202682014668223
pipglr-storage
pipglr-cache
8230fd623fc59d7621600304efcf1a11b5c9bf7cec5a8de5237b6d0143edb809 <i class="conum" data-value="2"></i><b>(2)</b>
</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>I really need to update this, meanwhile even my <a href="https://www.debian.org/">Debian</a> machine uses a decent version of Podman</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Yay!</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The output looks promising, so let us verify our containers via Podman:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman ps <span class="nt">-a</span> <span class="nt">--format</span> <span class="s1">'table {{.ID}} {{.Image}} {{.Status}} {{.Names}}'</span>

CONTAINER ID  IMAGE                                    STATUS                   NAMES
bfac4e6acb26  localhost/podman-pause:5.3.2-1737979078  Up 42 minutes            e91d11fdeb16-infra
cc6599fdf8db  docker.io/gitlab/gitlab-ce:latest        Up 42 minutes <span class="o">(</span>healthy<span class="o">)</span>  gitlab
8230fd623fc5  localhost/custom-pip-runner:latest       Up About a minute        pipglr</code></pre>
</div>
</div>
<div class="paragraph">
<p>And there it is, our new runner in the list of Gitlab:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/gitlab_with_podman/gitlab_runner.png" alt="gitlab runner">
</div>
<div class="title">Screenshot of our newly created runner</div>
</div>
<div class="paragraph">
<p>From here everything should be pretty much self-explanatory and there are loads of good articles
how to actually use Gitlab itself like:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="https://docs.gitlab.com/ee/tutorials/" class="bare">https://docs.gitlab.com/ee/tutorials/</a></p>
</li>
<li>
<p><a href="https://docs.gitlab.com/ee/ci/quick_start/" class="bare">https://docs.gitlab.com/ee/ci/quick_start/</a></p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="bonus-running-with-dagger">Bonus: Running with Dagger</h3>
<div class="paragraph">
<p>Following the original idea of using Dagger, just another step of preparation is required.
Dagger uses another container inside the runner and adds a bit more compexity to the mix:</p>
</div>
<p><object data='/uml/15f01b05eda673f79c3507d218a68376.svg' type='image/svg+xml'  class='plantuml'></object></p>
<div class="paragraph">
<p>The containers are nicely stacked, but this requires a specially grafted one for Dagger in order
for it to access files:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="dockerfile"><span class="k">FROM</span><span class="s"> docker.io/golang:alpine</span>

<span class="k">MAINTAINER</span><span class="s"> Christoph Kappel &lt;christoph@unexist.dev&gt;</span>

<span class="k">RUN </span>apk add podman podman-docker curl fuse-overlayfs <span class="se">\
</span>    <span class="o">&amp;&amp;</span> <span class="nb">sed</span> <span class="nt">-i</span> <span class="s1">'s/#mount_program/mount_program/'</span> /etc/containers/storage.conf <span class="se">\ </span><i class="conum" data-value="1"></i><b>(1)</b>
    &amp;&amp; curl -sL --retry 3 https://dl.dagger.io/dagger/install.sh | BIN_DIR=/usr/local/bin sh</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This took me quite a while to figure out</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="bonus-caching-via-registry">Bonus: Caching via registry</h3>
<div class="paragraph">
<p>With so many containers (1x gitlab + 1x runner + 1x builder) the limit of a free tier can be
quicky reached, and it is strongly advised to add some kind of caching layer.
Gitlab comes with its own <a href="https://docs.gitlab.com/ee/user/packages/container_registry/">registry</a> and can be used to cache all artifacts locally.</p>
</div>
<div class="paragraph">
<p>We already did the required configuration in our minimal config, so we just have to push the
containers and configure the registry.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman login <span class="nt">-u</span> root <span class="nt">-p</span> <span class="si">$(</span>GITLAB_PASS<span class="si">)</span> <span class="nt">--tls-verify</span><span class="o">=</span><span class="nb">false </span>https://<span class="si">$(</span>REGISTRY_URL<span class="si">)</span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="nv">$ </span>podman push <span class="nt">--tls-verify</span><span class="o">=</span><span class="nb">false</span> <span class="se">\</span>
    <span class="si">$(</span>BUILDER_IMAGE_NAME<span class="si">)</span>:latest <span class="si">$(</span>REGISTRY_URL<span class="si">)</span>/root/showcase-dagger-golang/<span class="si">$(</span>BUILDER_IMAGE_NAME<span class="si">)</span>:latest</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Perfectly set-up environment for sure!</td>
</tr>
</table>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
And finally this can be done with the make target <code>pd-gitlab-prepare-cache</code>.
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Gitlab is by itself a complex system and adding Podman and Dagger to the mix doesn&#8217;t make it
easer at all, but probably increases the complexy tenfold.</p>
</div>
<div class="paragraph">
<p><strong>So what do we actually get?</strong></p>
</div>
<div class="paragraph">
<p>During my experiments with the trio I quickly ran into many problems and some of them were really
challenging.
Although I tried to address some of them in this blog post, to make it fellow readers easier to
gets started, the whole thing is still complicated.</p>
</div>
<div class="paragraph">
<p>My original goal was to benefit from the facts to have pipeline knowledge everywhere, since the
same pipelines are run locally and in the actual CICD and to be freed from the sales stuff
of Docker, but if I consider the cost of this small advantage&#8230;&#8203;</p>
</div>
<div class="paragraph">
<p>Ultimately I made the decision to postpone every move in this direction for now.</p>
</div>
<div class="paragraph">
<p>All examples can be found next to the examples from the first post:</p>
</div>
<div class="paragraph">
<p><a href="https://github.com/unexist/showcase-dagger-golang" class="bare">https://github.com/unexist/showcase-dagger-golang</a></p>
</div>
</div>
</div>]]></content><author><name>Christoph Kappel</name></author><category term="cicd" /><category term="gitlab" /><category term="podman" /><category term="dagger" /><category term="showcase" /><summary type="html"><![CDATA[This blog post is a spin-off of the original post about the programmable CICD system Dagger and explains how to set up Gitlab with Podman.]]></summary></entry><entry><title type="html">Bringing documentation together</title><link href="https://unexist.blog/documentation/myself/2024/12/26/bringing-documentation-together.html" rel="alternate" type="text/html" title="Bringing documentation together" /><published>2024-12-26T18:20:00+01:00</published><updated>2024-12-26T18:20:00+01:00</updated><id>https://unexist.blog/documentation/myself/2024/12/26/bringing-documentation-together</id><content type="html" xml:base="https://unexist.blog/documentation/myself/2024/12/26/bringing-documentation-together.html"><![CDATA[<div class="paragraph">
<p>Documentation is and was always my strong point and if I look back upon the year, which is about to
close, it also has been a huge part inside of this blog and my daily job.
During the year one critical problem (among how to <span class="line-through">motivate</span> create a motivating
environment to write documentation) remained:</p>
</div>
<div class="paragraph">
<p><em>How can we manage documentation that is scattered among many repositories and documentation
systems?</em></p>
</div>
<div class="paragraph">
<p>The first problem is easily solved and I also recommended giving <a href="https://antora.org/">Antora</a> a spin for my go-to
documentation system <a href="https://asciidoc.org/">AsciiDoc</a>
<a href="https://unexist.blog/tools/2021/06/24/aggregate-asciidoc-from-multiple-repositories.html">here</a>,
but what about the latter?</p>
</div>
<div class="paragraph">
<p>If you look closely, you can probably find <strong>n+1</strong> documentation systems for every language.
Examples include <a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html">Javadoc</a> for <a href="https://www.java.com/en/">Java</a>, <a href="https://doc.rust-lang.org/rustdoc/index.html">Rustdoc</a> for <a href="https://www.rust-lang.org/">Rust</a> just to name a few I daily
use.
Visiting all of them is totally beyond the scope of this post, so this post focuses on a more
general approach with <a href="https://www.doxygen.nl">Doxygen</a>, which also better matches my main motivation to align
documentation for application and embedded software engineering.</p>
</div>
<div class="sect1">
<h2 id="what-is-doxygen">What is Doxygen?</h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://www.doxygen.nl">Doxygen</a> was actually the first documentation generator I&#8217;ve ever used and even my oldest
C project <a href="https://github.com/unexist/subtle">subtle</a> contains configuration for it.</p>
</div>
<div class="paragraph">
<p>In a nutshell Doxygen collects special <a href="https://www.doxygen.nl/manual/docblocks.html#specialblock">comment blocks</a> from the actual source files, takes care
of all the symbols and provides various output formats like HTML in the next example:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="c"> <span class="cm">/**
  * @brief Main function <i class="conum" data-value="1"></i><b>(1)</b>
  *
  * @details <i class="conum" data-value="2"></i><b>(2)</b>
  * @startuml
  * main.c -&gt; lang.c : get_lang()
  * @enduml
  *
  * @param[in]  argc  Number of arguments <i class="conum" data-value="3"></i><b>(3)</b>
  * @param[in]  argv  Array with passed commandline arguments
  * @retval  0  Default return value <i class="conum" data-value="4"></i><b>(4)</b>
  **/</span>

 <span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">argv</span><span class="p">[])</span> <span class="p">{</span>

    <span class="n">printf</span><span class="p">(</span><span class="s">"Hello, %s"</span><span class="p">,</span> <span class="n">get_lang</span><span class="p">(</span><span class="s">"NL"</span><span class="p">));</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
 <span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The first section <a href="https://www.doxygen.nl/manual/commands.html#cmdbrief">brief</a> briefly (as the name implies) describes the method or function</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>A <a href="https://www.doxygen.nl/manual/commands.html#cmddetails">details</a> block includes more verbose information about the implementation in the source
file and can even contain <a href="https://github.com/plantuml/plantuml">Plantuml</a> diagrams</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td><a href="https://www.doxygen.nl/manual/commands.html#cmdparam">Parameters</a> should surprise no one besides the direction information <code>in</code>, <code>out</code> or both</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>And lastly <a href="https://www.doxygen.nl/manual/commands.html#cmdreturn">return values</a> can also be nicely laid out</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
Normally Doxygen command starts with a <b class="button">\</b>, but I personally prefer the Javadoc <b class="button">@</b>
version via the config option <code>JAVADOC_AUTOBRIEF</code>.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Doxygen can then be run either locally or even better via <a href="https://github.com/unexist/showcase-documentation-asciidoxy/blob/master/infrastructure/Containerfile">container</a> to create the first version
of our output:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> /home/unexist/projects/showcase-documentation-asciidoxy:/asciidoxy <span class="se">\</span>
    <span class="nt">-it</span> docker.io/unexist/asciidoxy-builder:0.3 <span class="se">\</span>
    sh <span class="nt">-c</span> <span class="s2">"cd /asciidoxy &amp;&amp; doxygen"</span>
Doxygen version used: 1.11.0
Searching <span class="k">for </span>include files...
Searching <span class="k">for </span>example files...
Searching <span class="k">for </span>images...
Searching <span class="k">for </span>dot files...
...
Generate XML output <span class="k">for </span><span class="nb">dir</span> /asciidoxy/src/
Running plantuml with JAVA...
Generating PlantUML png Files <span class="k">in </span>html
<span class="nb">type </span>lookup cache used 8/65536 <span class="nv">hits</span><span class="o">=</span>26 <span class="nv">misses</span><span class="o">=</span>8
symbol lookup cache used 16/65536 <span class="nv">hits</span><span class="o">=</span>50 <span class="nv">misses</span><span class="o">=</span>16
finished...</code></pre>
</div>
</div>
<div class="paragraph">
<p>Once done the generated html pages look like this (in dark mode):</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/bringing_documentation_together/doxygen.png" alt="doxygen">
</div>
<div class="title">Screenshot of the generated Doxygen docs</div>
</div>
<div class="paragraph">
<p>This works well, but unfortunately creates another documentation artifact somewhere and doesn&#8217;t
move us any closer to an aggregated documentation - <strong>yet</strong>.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="how-can-asciidoxy-help">How can AsciiDoxy help?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Besides the html output from above, Doxygen can also create xml files which include information
about all the found symbols, their documentation and also their relationship to each other.
Normally this would be quite messy to integrate into Asciidoc, but this is the gap <a href="https://asciidoxy.org/">AsciiDoxy</a>
closes as we are going to see next.</p>
</div>
<div class="paragraph">
<p>Originally created by <a href="https://www.tomtom.com/">TomTom</a> and hopefully still managed since I&#8217;ve opened a <a href="https://github.com/tomtom-international/asciidoxy/issues/124">bug</a> on
<a href="https://github.com/">Github</a>, it parses the xml files and ultimately provides a short list of AsciiDoc macros
for convenient use inside our documents:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="asciidoc">${language("cpp")} <i class="conum" data-value="1"></i><b>(1)</b>
${insert("main", leveloffset=2)} <i class="conum" data-value="2"></i><b>(2)</b>
${insert("main", template="customfunc")} <i class="conum" data-value="3"></i><b>(3)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Set the <a href="https://asciidoxy.org/reference/commands.html#_setting_default_programming_language">language</a> - the <a href="https://www.makotemplates.org/">Mako</a> templates vary a bit based on the language</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td><a href="https://asciidoxy.org/reference/commands.html">Insert</a> an actual symbol</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Insert the same symbol again, but use a different <a href="https://asciidoxy.org/getting-started/custom-templates.html">template</a> now</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
The initial setup is a bit tricky, especially with the different modules, but refer to the
showcase and the official manual if you are stuck.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The container from before is equipped with the whole chain, so let us quickly fire it up:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">-v</span> /home/unexist/projects/showcase-documentation-asciidoxy:/asciidoxy <span class="se">\</span>
    <span class="nt">-it</span> docker.io/unexist/asciidoxy-builder:0.3 <span class="se">\</span>
    sh <span class="nt">-c</span> <span class="s2">"cd /asciidoxy &amp;&amp; asciidoxy </span><span class="se">\</span><span class="s2">
    --require asciidoctor-diagram </span><span class="se">\</span><span class="s2">
    --spec-file packages.toml </span><span class="se">\</span><span class="s2">
    --base-dir text </span><span class="se">\</span><span class="s2">
    --destination-dir src/site/asciidoc </span><span class="se">\</span><span class="s2">
    --build-dir build </span><span class="se">\</span><span class="s2">
    --template-dir templates </span><span class="se">\</span><span class="s2">
    -b adoc </span><span class="se">\</span><span class="s2">
    text/index.adoc"</span>

    ___              _ _ ____             0.8.7
   /   |  __________<span class="o">(</span>_|_<span class="o">)</span> __ <span class="se">\_</span>___  _  ____  __
  / /| | / ___/ ___/ / / / / / __ <span class="se">\|</span> |/_/ / / /
 / ___ |<span class="o">(</span>__  <span class="o">)</span> /__/ / / /_/ / /_/ /&gt;  &lt;/ /_/ /
/_/  |_/____/<span class="se">\_</span>__/_/_/_____/<span class="se">\_</span>___/_/|_|<span class="se">\_</span>_, /
                                      /____/

Collecting packages     : 100%|██████████████████████████████████| 1/1 <span class="o">[</span>00:00&lt;00:00, 226.55pkg/s]
Loading API reference   : 100%|██████████████████████████████████| 1/1 <span class="o">[</span>00:00&lt;00:00, 47.60pkg/s]
Resolving references    : 100%|██████████████████████████████████| 2/2 <span class="o">[</span>00:00&lt;00:00, 1954.48ref/s]
Checking references     : 100%|██████████████████████████████████| 1/1 <span class="o">[</span>00:00&lt;00:00, 28149.69ref/s]
Preparing work directory: 100%|██████████████████████████████████| 2/2 <span class="o">[</span>00:00&lt;00:00, 267.69pkg/s]
Processing asciidoc     : 100%|██████████████████████████████████| 2/2 <span class="o">[</span>00:00&lt;00:00, 67.52file/s]
Copying images          : 100%|██████████████████████████████████| 2/2 <span class="o">[</span>00:00&lt;00:00, 6647.07pkg/s]</code></pre>
</div>
</div>
<div class="paragraph">
<p>Once this step is done AsciiDoxy has expanded all the macros and replaced them with the appropriate
AsciiDoc directives like the following for <code>${insert("main", leveloffset=2)}</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="asciidoc">[#cpp-hello_8c_1a0ddf1224851353fc92bfbff6f499fa97,reftext='main']
=== main


[%autofit]
[source,cpp,subs="-specialchars,macros+"]
----
#include &amp;lt;src/hello.c&amp;gt;

int main(int argc,
         char * argv)
----


main

Main function

[plantuml]
....
main.c -&gt; lang.c : get_lang()
....

[cols='h,5a']
|===
| Parameters
|
`int argc`::
Number of arguments

`char * argv`::
Array with passed commandline arguments

| Returns
|
`int`::


|===</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
The markup is a bit cryptic, but shouldn&#8217;t be too hard to understand with a bit of AsciiDoc
knowledge.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>AsciiDoxy can perfectly generate AsciiDoc documents by itself and even supports <a href="https://asciidoxy.org/getting-started/multipage.html">multipage</a>
documents, but we require an intermediate step for the next part.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="bringing-everything-together">Bringing everything together</h2>
<div class="sectionbody">
<div class="paragraph">
<p>There is more than one way to generate the prepared document to its final form, but as initially
told the general idea is to bring everything together.</p>
</div>
<div class="paragraph">
<p>I am not that fond of <a href="https://www.atlassian.com/software/confluence">Confluence</a>, but the goal of collecting everything in one place ranks
higher than my taste here.
Since rendering just the document doesn&#8217;t work here, we are going to rely on the
<a href="https://github.com/confluence-publisher/confluence-publisher">asciidoc-confluence-publisher-maven-plugin</a> from before.</p>
</div>
<div class="paragraph">
<p>This adds some more dependencies and finally explains why the container is based on <a href="https://maven.apache.org/">Maven</a>.</p>
</div>
<div class="paragraph">
<p>The base call to create the document works in the same manner as before:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">--dns</span> 8.8.8.8 <span class="nt">-v</span> /home/unexist/projects/showcase-documentation-asciidoxy:/asciidoxy <span class="se">\</span>
    <span class="nt">-it</span> docker.io/unexist/asciidoxy-builder:0.3 <span class="se">\</span>
    sh <span class="nt">-c</span> <span class="s2">"cd /asciidoxy &amp;&amp; mvn -f pom.xml generate-resources"</span>
<span class="o">[</span>INFO] Scanning <span class="k">for </span>projects...
<span class="o">[</span>INFO]
<span class="o">[</span>INFO] <span class="nt">--------------</span>&lt; dev.unexist.showcase:showcase-documentation-asciidoxy <span class="o">&gt;</span><span class="nt">---------------</span>
<span class="o">[</span>INFO] Building showcase-documentation-asciidoxy 0.1
<span class="o">[</span>INFO]   from pom.xml
<span class="o">[</span>INFO] <span class="nt">--------------------------------</span><span class="o">[</span> jar <span class="o">]</span><span class="nt">---------------------------------</span>
Downloading from central: https://repo.maven.apache.org/maven2/org/asciidoctor/asciidoctor-maven-plugin/2.1.0/asciidoctor-maven-plugin-2.1.0.pom
...
<span class="o">[</span>INFO] Using <span class="s1">'UTF-8'</span> encoding to copy filtered resources.
<span class="o">[</span>INFO] Copying 2 resources
<span class="o">[</span>INFO] asciidoctor: WARN: index.adoc: line 60: <span class="nb">id </span>assigned to section already <span class="k">in </span>use: cpp-hello_8c_1a0ddf1224851353fc92bfbff6f499fa97
<span class="o">[</span>INFO] Converted /asciidoxy/src/site/asciidoc/index.adoc
<span class="o">[</span>INFO] <span class="nt">------------------------------------------------------------------------</span>
<span class="o">[</span>INFO] BUILD SUCCESS
<span class="o">[</span>INFO] <span class="nt">------------------------------------------------------------------------</span>
<span class="o">[</span>INFO] Total <span class="nb">time</span>:  17.596 s
<span class="o">[</span>INFO] Finished at: 2024-12-26T15:51:23Z
<span class="o">[</span>INFO] <span class="nt">------------------------------------------------------------------------</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>And if we have a look at our final result:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/bringing_documentation_together/asciidoc.png" alt="asciidoc">
</div>
<div class="title">Screenshot of the generated AsciiDoc docs</div>
</div>
<div class="paragraph">
<p>Getting the actual document to Confluence is a nice exercise for my dear readers:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>podman run <span class="nt">--rm</span> <span class="nt">--dns</span> 8.8.8.8 <span class="nt">-v</span> /home/unexist/projects/showcase-documentation-asciidoxy:/asciidoxy <span class="se">\</span>
        <span class="nt">-it</span> docker.io/unexist/asciidoxy-builder:<span class="si">$(</span>VERSION<span class="si">)</span> <span class="se">\</span>
        <span class="nt">-e</span> <span class="nv">CONFLUENCE_URL</span><span class="o">=</span><span class="s2">"unexist.blog"</span> <span class="se">\</span>
        <span class="nt">-e</span> <span class="nv">CONFLUENCE_SPACE_KEY</span><span class="o">=</span><span class="s2">"UXT"</span> <span class="se">\</span>
        <span class="nt">-e</span> <span class="nv">CONFLUENCE_ANCESTOR_ID</span><span class="o">=</span><span class="s2">"123"</span> <span class="se">\</span>
        <span class="nt">-e</span> <span class="nv">CONFLUENCE_USER</span><span class="o">=</span><span class="s2">"unexist"</span> <span class="se">\</span>
        <span class="nt">-e</span> <span class="nv">CONFLUENCE_TOKEN</span><span class="o">=</span><span class="s2">"secret123"</span> <span class="se">\</span>
        sh <span class="nt">-c</span> <span class="s2">"cd </span><span class="si">$(</span>MOUNTPATH<span class="si">)</span><span class="s2"> &amp;&amp; mvn -f pom.xml -P generate-docs-and-publish generate-resources"</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>Give it a try, I&#8217;ll watch.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Adding Doxygen and AsciiDoxy to the mix allows us to enhance our documentation with rendered meta
information directly from the code and supplements the existing features of directly including
code by file or tag.
Being able to customize the used templates and select per symbol what is included offers great
flexibility and still keeps the beautiful look of AsciiDoc.</p>
</div>
<div class="paragraph">
<p>The additional overhead of the toolchain and the intermediate steps to call Doxygen, AsciDoxy and
AsciiDoc on every change is something to consider, but should be a no-brainer within a proper
CICD pipeline.</p>
</div>
<div class="paragraph">
<p>All examples can be found here:</p>
</div>
<div class="paragraph">
<p><a href="https://github.com/unexist/showcase-documentation-asciidoxy" class="bare">https://github.com/unexist/showcase-documentation-asciidoxy</a></p>
</div>
</div>
</div>]]></content><author><name>Christoph Kappel</name></author><category term="documentation" /><category term="asciidoc" /><category term="doxygen" /><category term="asciidoxy" /><category term="showcase" /><summary type="html"><![CDATA[This blog post explains how documentation from Doxygen and AsciiDoc can be brought together with the help of AsciiDoxy.]]></summary></entry><entry><title type="html">Decision records</title><link href="https://unexist.blog/documentation/myself/2024/10/25/decision-records.html" rel="alternate" type="text/html" title="Decision records" /><published>2024-10-25T17:48:00+02:00</published><updated>2024-10-25T17:48:00+02:00</updated><id>https://unexist.blog/documentation/myself/2024/10/25/decision-records</id><content type="html" xml:base="https://unexist.blog/documentation/myself/2024/10/25/decision-records.html"><![CDATA[<div class="paragraph">
<p>I can probably cite myself from this blog, but writing documentation (not necessarily good
documentation mind you, but any at all) is really difficult and keeping it up-to-date nigh on
impossible.
To ease the pain, some clever people invented tools to write <a href="https://docsascode.org/">documentation-as-code</a>, so docs can
co-exist next to the source and have a better chance of being touched, whenever something is
changed.</p>
</div>
<div class="paragraph">
<p>Based on my personal experience I can say the same is true for any kind of project decisions and
good luck finding any hint about them - until I discovered records.</p>
</div>
<div class="sect1">
<h2 id="record-types">Record types</h2>
<div class="sectionbody">
<div class="paragraph">
<p>During the course of this post we are going to do a quick recap of ADR mostly by pointing to
links (<a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a>, you know?), introduce a new type for <a href="https://en.wikipedia.org/wiki/Technical_debt">technical debt</a> (aptly named <a href="https://github.com/ms1963/TechnicalDebtRecords">TDR</a>),
have a look at some examples some examples with adapted tooling and talk a bit about the power
of the idea, that isn&#8217;t covered by the documents alone.</p>
</div>
<div class="sect2">
<h3 id="architecture-decision-records">Architecture Decision Records</h3>
<div class="paragraph">
<p>When I first heard about
<a href="https://unexist.blog/documentation/myself/2020/09/15/architecture-decisions.html">architecture decisions</a>
I was directly intrigued and blogged about, so there is no need to reiterate on this right now,
but in hindsight I can say it really took a while for me to actually see the real benefit of them.</p>
</div>
<div class="paragraph">
<p>It never came to my mind, but why should we stop here?</p>
</div>
</div>
<div class="sect2">
<h3 id="technical-debts-records">Technical Debts Records</h3>
<div class="paragraph">
<p><a href="https://github.com/ms1963">Michael Stal</a> pretty much got the gist of it and his suggestion is to handle technical debt in
the same lieu as architecture decisions.</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Documented as code</p>
</li>
<li>
<p>Well placed next to the actual code or any other kind of source code repository</p>
</li>
<li>
<p>With some mandatory fields and an open format as a guide rail.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>In comparison with architecture decision records, the format of these new records (especially since
it is <a href="https://en.wikipedia.org/wiki/Markdown">Markdown</a>) looks a bit different, but we are going to cover that later on.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
I included the descriptions of the fields in the actual document, just because I cannot
explain the fields any better.
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="markdown"><span class="gu">Technical Debt Record
====================
</span>
<span class="gh">Title:
------
</span>A concise name for the technical debt.

<span class="gh">Author:
-------
</span>The individual who identified or is documenting the debt.

<span class="gh">Version:
--------
</span>The version of the project or component where the debt exists.

<span class="gh">Date:
-----
</span>The date when the debt was identified or recorded.

<span class="gh">State:
------
</span>The current workflow stage of the technical debt (e.g., Identified, Analyzed, Approved, In Progress, Resolved, Closed, Rejected).

<span class="gh">Relations:
----------
</span>Links to other related TDRs to establish connections between different debt items.

<span class="gh">Summary:
--------
</span>A brief overview explaining the nature and significance of the technical debt.

<span class="gh">Context:
--------
</span>Detailed background information, including why the debt was incurred (e.g., time constraints, outdated technologies).

<span class="gh">Impact:
-------
</span>Technical Impact:
<span class="p">-</span> How the debt affects system performance, scalability, maintainability, etc.

Business Impact:
<span class="p">-</span> The repercussions on business operations, customer satisfaction, risk levels, etc.

<span class="gh">Symptoms:
---------
</span>Observable signs indicating the presence of technical debt (e.g., frequent bugs, slow performance).

<span class="gh">Severity:
---------
</span>The criticality level of the debt (Critical, High, Medium, Low).

<span class="gh">Potential Risks:
----------------
</span>Possible adverse outcomes if the debt remains unaddressed (e.g., security vulnerabilities, increased costs).

<span class="gh">Proposed Solution:
-------------------
</span>Recommended actions or strategies to resolve the debt.

<span class="gh">Cost of Delay:
---------------
</span>Consequences of postponing the resolution of the debt.

<span class="gh">Effort to Resolve:
-------------------
</span>Estimated resources, time, and effort required to address the debt.

<span class="gh">Dependencies:
-------------
</span>Other tasks, components, or external factors that the resolution of the debt depends on.

<span class="gh">Additional Notes:
-----------------
</span>Any other relevant information or considerations related to the debt.</code></pre>
</div>
</div>
<div class="paragraph">
<p>He also provides <a href="https://github.com/ms1963/TechnicalDebtRecords">tooling</a> along with the definition, which is quite nice for a starter.</p>
</div>
<div class="paragraph">
<p>The format is really close to the one of the ADR, so I did the obvious migration and adapted it
to the format already used there.</p>
</div>
<div class="paragraph">
<p>The drawback of this is the previous tools cannot handle this new format and the adr-tools cannot
handle TDR yet.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="record-tools">Record tools</h2>
<div class="sectionbody">
<div class="paragraph">
<p>During the course of the last few years I played with the original <a href="https://github.com/npryce/adr-tools">adr-tools</a> based on the
work of its inventor <a href="https://github.com/npryce">Nat Pryce</a> and added some missing features.
Like the pending <a href="https://asciidoc.org/">Asciidoc</a> support, a simple database layer to speed up some of the generators
and added simple rss/atom feeds for easier aggregation.</p>
</div>
<div class="paragraph">
<p>This put me in a perfect position to adapt the tools even further and hack a new format into it
under a new umbrella.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
I am still playing with the idea to port the shellscripts to <a href="https://www.rust-lang.org/">Rust</a> - does anyone fancy
<code>record-tools-rs</code>?
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The following examples demonstrates how the record-tools can be used, starting with the basic steps
up to deploying rendered versions to a <a href="https://confluence.com/">Confluence</a> instance, since it always pays off to include
non-tech-savvy folks.</p>
</div>
<div class="paragraph">
<p>The record-tools include two examples, one of each kind to kickstart the decision to actually use
these formats and keep the intention of the original along with some shameful self advertisement:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="asciidoc">= 1. Record architecture decisions

:1: https://unexist.blog/documentation/myself/2024/10/22/decision-records.html

|===
| Proposed Date: | 2024-10-24
| Decision Date: | 2024-10-24
| Proposer:      | Christoph Kappel
| Deciders:      | Christoph Kappel
| Status:        | accepted
| Issues:        | none
| References:    | none
| Priority:      | high
|===

NOTE: *Status types:* drafted | proposed | rejected | accepted | deprecated | superseded +
      *Priority:* low | medium | high

== Context

We need to record the architectural decisions made on this project.

== Proposed Solution

Architecture Decision Records as {1}[summarised by Christoph] might help us as a format.

== Decision

We will use Architecture Decision Records.

== Consequences

None foreseeable.

== Further Information

== Comments</code></pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
<div class="paragraph">
<p>It isn&#8217;t strictly necessary to checkout the example, but if you want to play with the tooling:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>hg clone https://hg.unexist.dev/record-tools
<span class="nv">$ </span><span class="c"># OR: git clone https://github.com/unexist/record-tools</span>
...
<span class="nv">$ </span><span class="nb">cd </span>record-tools/example</code></pre>
</div>
</div>
</td>
</tr>
</table>
</div>
<div class="sect2">
<h3 id="create-new-records">Create new records</h3>
<div class="paragraph">
<p>Besides the name, the record-tools basically behave in the same manner like the original version
of the tools and for example a new TDB can be created like this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>../src/record-tdb new Usage of log4j <i class="conum" data-value="1"></i><b>(1)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This command creates a new record and opens it in your default $EDITOR</td>
</tr>
</table>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/decision_records/tdb-log4j.png" alt="tdb log4j">
</div>
<div class="title">Vim with a lovely default color scheme</div>
</div>
<div class="paragraph">
<p>If you consider the topic of this record there probably comes a lot to your mind what you would
like to add, but let us shorten this phase and accept the record as-is and press
<span class="line-through">save</span> <b class="button">:</b>+<b class="button">w</b>.</p>
</div>
</div>
<div class="sect2">
<h3 id="supersede-old-records">Supersede old records</h3>
<div class="paragraph">
<p>Sometimes decisions have to be revised (or superseded) and that couldn&#8217;t be more true with
technical matters, once more information has been gathered and/or experience with the actual
decision could be gained.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>../src/record-tdr new <span class="nt">-s</span> 2 Usage of zerolog <i class="conum" data-value="1"></i><b>(1)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Both are quite incompatible, but <a href="https://github.com/rs/zerolog">zerolog</a> is always worth mentioning</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="link-records">Link records</h3>
<div class="paragraph">
<p>Under the hood, <em>supersede</em> just overwrites the status of the previous record with <strong>supersded</strong> and
applies links in both directions.
This can also be done manually with arbitrary links:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>./src/record-tdr <span class="nb">link </span>3 Amends 1 <span class="s2">"Amended by"</span> <i class="conum" data-value="1"></i><b>(1)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This command links record 3 to 1 long with the relationship of the link forwards and backwards</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>There isn&#8217;t much direct visible effect besides the addition of the links to the
<strong>Further Information</strong> field, but more on this in the next section:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="asciidoc">== Further Information

Any other relevant information or considerations related to the debt.

Supersedes link:0002-usage-of-log4j.adoc[2. Usage of Log4j]

Amends link:0001-technical-debt-decision.adoc[1. Record technical debt decisions]</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="using-generators">Using generators</h3>
<div class="paragraph">
<p>The tools include various generators that can be used to generate listings, graphs and even feeds.</p>
</div>
<div class="sect3">
<h4 id="table-of-contents-toc">Table of Contents (TOC)</h4>
<div class="paragraph">
<p>The table of contents generates a nice overview of the known records and can additionally prepend and
append an intro and an outro, to allow further customization:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>../src/record-tdr generate toc <span class="nt">-i</span> Intro <span class="nt">-o</span> Outro
<span class="o">=</span> TDR records

Intro

<span class="k">*</span> <span class="nb">link</span>:0001-technical-debt-decision.adoc[1. Record technical debt decisions]
<span class="k">*</span> <span class="nb">link</span>:0002-usage-of-log4j.adoc[2. Usage of log4j]
<span class="k">*</span> <span class="nb">link</span>:0003-usage-of-zerolog.adoc[3. Usage of zerolog]

Outro</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="atom-rss">Atom &amp; RSS</h4>
<div class="paragraph">
<p>These two generators should be pretty self-explanatory:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>../src/record-tdr generate rss <i class="conum" data-value="1"></i><b>(1)</b>
&lt;?xml <span class="nv">version</span><span class="o">=</span><span class="s2">"1.0"</span> <span class="nv">encoding</span><span class="o">=</span><span class="s2">"UTF-8"</span> ?&gt;
&lt;rss <span class="nv">version</span><span class="o">=</span><span class="s2">"2.0"</span><span class="o">&gt;</span>
  &lt;channel&gt;
    &lt;title&gt;List of all tdr records&lt;/title&gt;
    &lt;description&gt;List of all created tdr records&lt;/description&gt;
    &lt;ttl&gt;240&lt;/ttl&gt;
    &lt;lastBuildDate&gt;2024-10-24 12:05&lt;/lastBuildDate&gt;
    &lt;generator&gt;record-tools&lt;/generator&gt;
    &lt;webmaster&gt;christoph@unexist.dev&lt;/webmaster&gt;
&lt;item&gt;&lt;title&gt;1. Record technical debt decisions&lt;/title&gt;&lt;<span class="nb">link</span><span class="o">&gt;</span>0001-technical-debt-decision.adoc&lt;/link&gt;&lt;category&gt;high&lt;/category&gt;&lt;pubDate&gt;2024-10-24&lt;/pubDate&gt;&lt;description&gt;Status: superseded&lt;/description&gt;&lt;/item&gt; &lt;item&gt;&lt;title&gt;2. Usage of log4j&lt;/title&gt;&lt;<span class="nb">link</span><span class="o">&gt;</span>0002-usage-of-log4j.adoc&lt;/link&gt;&lt;category&gt;low&lt;/category&gt;&lt;pubDate&gt;2024-10-22&lt;/pubDate&gt;&lt;description&gt;Status: superseded&lt;/description&gt;&lt;/item&gt; &lt;item&gt;&lt;title&gt;3. Usage of zerolog&lt;/title&gt;&lt;<span class="nb">link</span><span class="o">&gt;</span>0003-usage-of-zerolog.adoc&lt;/link&gt;&lt;category&gt;low&lt;/category&gt;&lt;pubDate&gt;2024-10-23&lt;/pubDate&gt;&lt;description&gt;Status: drafted&lt;/description&gt;&lt;/item&gt;
  &lt;/channel&gt;
&lt;/rss&gt;</code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Either use <code>rss</code> <code>atom</code> for the specific type</td>
</tr>
</table>
</div>
</div>
<div class="sect3">
<h4 id="digraph-plantuml">Digraph &amp; Plantuml</h4>
<div class="paragraph">
<p>Both generators create a graph based on <a href="https://graphviz.org/doc/info/lang.html">dot</a> - the sole difference is the plantuml version just
neatly wraps the output between <code>@startdot</code> and <code>@enddot</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>../src/record-tdr generate plantuml
... <i class="conum" data-value="1"></i><b>(1)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>We omit the output here, because it looks way better directly rendered with <a href="https://github.com/plantuml/plantuml">Plantuml</a> below</td>
</tr>
</table>
</div>
<p><object data='/uml/b90d745f4cc220ec61176dd9d7895370.svg' type='image/svg+xml'  class='plantuml'></object></p>
<div class="paragraph">
<p>Plantuml doesn&#8217;t use the passed links, but when the graph is directly renderes as a a vector graphic
(<a href="https://en.wikipedia.org/wiki/SVG">svg</a>) it also includes links:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>../src/record-tdr generate digraph | dot <span class="nt">-Tsvg</span> <span class="o">&gt;</span> graph.svg</code></pre>
</div>
</div>
</div>
<div class="sect3">
<h4 id="index">Index</h4>
<div class="paragraph">
<p>And index accumulates all known records, groups them based on different properties like
the severity and combines everything into a clickable page.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<i class="fa icon-note" title="Note"></i>
</td>
<td class="content">
This uses the tools quite heavily - or in other words is pretty slow.
Therefore it relies on the database to speed things up, which needs to be populated first.
</td>
</tr>
</table>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>../src/record-tdr generate database
<span class="nv">$ </span>../src/record-tdr generate index
...
<span class="o">==</span> List of all TDR with high severity

<span class="o">[</span><span class="nv">cols</span><span class="o">=</span><span class="s2">"3,1,1,1,1"</span>, <span class="nv">options</span><span class="o">=</span><span class="s2">"header"</span><span class="o">]</span>
|<span class="o">===</span>
|Name|Proposed Date|Decision Date|Status|Severity
|<span class="o">&lt;&lt;</span><span class="no">technical</span><span class="sh">-debt-records/0001-technical-debt-decision.adoc#, 1. Record technical debt decisions&gt;&gt;|2024-10-24|2024-10-24|superseded|high
|===

== List of all TDR with critical severity

[cols="3,1,1,1,1", options="header"]
|===
|Name|Proposed Date|Decision Date|Status|Severity

|===

== List of all TDR

[cols="3,1,1,1,1", options="header"]
|===
|Name|Proposed Date|Decision Date|Status|Severity
|&lt;&lt;technical-debt-records/0001-technical-debt-decision.adoc#, 1. Record technical debt decisions&gt;&gt;|2024-10-24|2024-10-24|superseded|high
|&lt;&lt;technical-debt-records/0002-usage-of-log4j.adoc#, 2. Usage of log4j&gt;&gt;|2024-10-24|?|superseded|low
|&lt;&lt;technical-debt-records/0003-usage-of-zerolog.adoc#, 3. Usage of zerolog&gt;&gt;|2024-10-24|?|drafted|low
|===
...</span></code></pre>
</div>
</div>
<div class="paragraph">
<p>This page can be converted via <a href="https://asciidoctor.org/">Asciidoctor</a> and its various backends:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>../src/record-adr generate database <i class="conum" data-value="1"></i><b>(1)</b>
<span class="nv">$ </span>../src/record-adr generate index <span class="o">&gt;</span> _adr_autogen.adoc <i class="conum" data-value="2"></i><b>(2)</b>
<span class="nv">$ </span>asciidoctor <span class="nt">-D</span> architecture-decision-records src/site/asciidoc/architecture-decision-records/<span class="k">*</span>.adoc <i class="conum" data-value="3"></i><b>(3)</b>
<span class="nv">$ </span>asciidoctor <span class="nt">-D</span> <span class="nb">.</span> <span class="nt">-I</span> architecture-decision-records /site/asciidoc/architecture-decision-records.adoc <i class="conum" data-value="4"></i><b>(4)</b>
<span class="nv">$ </span>asciidoctor <span class="nt">-r</span> asciidoctor-pdf <span class="nt">-b</span> pdf <span class="nt">-D</span> <span class="nb">.</span> src/site/asciidoc/architecture-decision-records.adoc <i class="conum" data-value="5"></i><b>(5)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Generate the database for both types</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Generate a neat index page for both types</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Render the actual documents now</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Optional step - just in case a PDF version is required</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Once rendered the pages should look like this:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/decision_records/index-page.png" alt="index page">
</div>
<div class="title">Index page</div>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/decision_records/adr-page.png" alt="adr page">
</div>
<div class="title">ADR page</div>
</div>
<div class="paragraph">
<p>Another way of generating the page is via <a href="https://maven.apache.org/">Maven</a>, which is quite handy since it is
prerequisite for the next step anyway.
Fortunately the example contains all required configuration and all that needs to be done is this:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span>mvn <span class="nt">-P</span> generate-docs <span class="nb">exec</span>:exec generate-resources <i class="conum" data-value="1"></i><b>(1)</b></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The <a href="https://www.mojohaus.org/exec-maven-plugin/">maven exec plugin</a> handles the database generation and index page part</td>
</tr>
</table>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<i class="fa icon-tip" title="Tip"></i>
</td>
<td class="content">
There is a Makefile included in the example that provides convenience targets for the
commands like <code>make generate</code> and <code>make publish</code> which will come in handy for the next step.
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect2">
<h3 id="publish-everything">Publish everything</h3>
<div class="paragraph">
<p>And finally we want to publish our documents, to make them easy accessible for everyone.
There are many different options to pick from, but one of the easiest is to use the
<a href="https://confluence-publisher.atlassian.net/wiki/spaces/CPD/overview?mode=global">Confluence Publisher</a> and put our documents to a Confluence instance of our choice.</p>
</div>
<div class="paragraph">
<p>Spinning up a confluence instance for this example is quite pointless without a license, so if
you really want to see it in action there is some config required in the <code>pom.xml</code> file:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="xml"><span class="c">&lt;!-- Confluence config --&gt;</span>
<span class="c">&lt;!-- NOTE: Be careful with the ancestorID, everything will be overwritten --&gt;</span>
<span class="nt">&lt;confluence.url&gt;</span>${env.CONFLUENCE_URL}<span class="nt">&lt;/confluence.url&gt;</span> <i class="conum" data-value="1"></i><b>(1)</b>
<span class="nt">&lt;confluence.publishingStrategy&gt;</span>APPEND_TO_ANCESTOR<span class="nt">&lt;/confluence.publishingStrategy&gt;</span>

<span class="c">&lt;!-- Provide these values from env; don't commit them! --&gt;</span>
<span class="nt">&lt;confluence.spaceKey&gt;</span>${env.CONFLUENCE_SPACE}<span class="nt">&lt;/confluence.spaceKey&gt;</span> <i class="conum" data-value="2"></i><b>(2)</b>
<span class="nt">&lt;confluence.ancestorId&gt;</span>${env.CONFLUENCE_ANCESTOR}<span class="nt">&lt;/confluence.ancestorId&gt;</span> <i class="conum" data-value="3"></i><b>(3)</b>
<span class="nt">&lt;confluence.publisherUserName&gt;</span>${env.CONFLUENCE_USER}<span class="nt">&lt;/confluence.publisherUserName&gt;</span>
<span class="nt">&lt;confluence.publisherPassword&gt;</span>${env.CONFLUENCE_TOKEN}<span class="nt">&lt;/confluence.publisherPassword&gt;</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The configuration can either passed by environment variables or be hardcoded - this is up to you</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>This is normally the two letter abbreviation of the space, which can be found within the
space settings</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>And finally we also need the ancestor id to append our records to. Problems to find it? Just
open the page settings and have a look at the address bar of your browser.</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>And once everything is set up correctly just fire up following:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ CONFLUENCE_USER</span><span class="o">=</span>USER_NAME <span class="nv">CONFLUENCE_TOKEN</span><span class="o">=</span>USER_TOKEN mvn <span class="nt">-P</span> generate-docs-and-publish <span class="nb">exec</span>:exec generate-resources</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="records-and-culture">Records and culture</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Aside from the documentation aspect and way to have these documents kind of guided to the guided
document layout, we haven&#8217;t spoken of the real power of this yet.</p>
</div>
<div class="paragraph">
<p>Records foster active collaboration and work splendidly with all kind of crowd thinking.
They offer a space to experiment maybe in the form of <a href="https://en.wikipedia.org/wiki/Proof_of_concept">proof-of-concepts</a> or simple showcase
for a particular technologie or to collect further opinions in <a href="https://en.wikipedia.org/wiki/Writers_workshop_(activity)">Writer&#8217;s Workshops</a>.</p>
</div>
<div class="paragraph">
<p>In this way teams are able to contribute to and suggest changes of the overall architecture in the
case of ADR and point to critical problems within TDR.
This can be a culture change of the involved teams, since it allows a more active participation
in the process and especially if they are involved in the actual (democratic?) decision.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>We are still experimenting with the actual documents and formats at work, but my personal feeling
is this really moves us forward and allows the team more autonomy and offers additional ways for
contribution.</p>
</div>
<div class="paragraph">
<p>Like always all my examples can be found here:</p>
</div>
<div class="paragraph">
<p><a href="https://github.com/unexist/record-tools" class="bare">https://github.com/unexist/record-tools</a></p>
</div>
</div>
</div>]]></content><author><name>Christoph Kappel</name></author><category term="documentation" /><category term="adr" /><category term="tdr" /><category term="asciidoc" /><category term="markdown" /><category term="showcase" /><summary type="html"><![CDATA[This blog post summarizes my experience with architecture decision records (ADR), introduces technical debt records (TDB) and demonstrates how everything can be nicely combined document and culture wise.]]></summary></entry><entry><title type="html">Monitoring with SigNoz</title><link href="https://unexist.blog/observability/2024/09/24/monitoring-with-signoz.html" rel="alternate" type="text/html" title="Monitoring with SigNoz" /><published>2024-09-24T17:57:00+02:00</published><updated>2024-09-24T17:57:00+02:00</updated><id>https://unexist.blog/observability/2024/09/24/monitoring-with-signoz</id><content type="html" xml:base="https://unexist.blog/observability/2024/09/24/monitoring-with-signoz.html"><![CDATA[<div class="paragraph">
<p>Finding good reasoning to explore different options for monitoring or better [observability] is
difficult.
Either there wasn&#8217;t the singular impact on production yet, that made you lust for better monitoring
and/or it is difficult to understand the merit and invest of time.</p>
</div>
<div class="paragraph">
<p>And even when you make the decision to dive into it, it is always a good idea not to start on
production, but with a simple example.
Simple examples on the other hand rarely show the real powers, so it usually ends in heavily
contrived ones like the one I&#8217;ve used in my last post about
<a href="https://unexist.blog/observability/2022/02/18/logging-vs-tracing.html">Logging vs Tracing</a>.</p>
</div>
<div class="paragraph">
<p>Still, <span class="line-through"><a href="https://www.forbes.com/sites/duenablomstrom1/2018/11/30/nobody-gets-fired-for-buying-ibm-but-they-should/">nobody got fired for buying IBM</a></span> ramping up monitoring, so let us - for
the course of this post - put our <a href="https://www.digitalocean.com/community/tutorials/elasticsearch-fluentd-and-kibana-open-source-log-search-and-visualization">EFK</a> stack and friends aside and get started with something
shiny new in <a href="https://go.dev/">Golang</a>.</p>
</div>
<div class="sect1">
<h2 id="what-is-signoz">What is SigNoz?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If you are like me and you haven&#8217;t heard the name <a href="https://signoz.io/">SigNoz</a> before the first and foremost
questions are probably what is SigNoz and why not one of these solutions
<code>insert random product here</code>.</p>
</div>
<div class="paragraph">
<p>From a marketing perspective the key selling point for me probably and honestly was the headline
on the frontpage:</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>OpenTelemetry-Native Logs, Metrics and Traces in a single pane</p>
</div>
</blockquote>
<div class="attribution">
&#8212; https://signoz.io
</div>
</div>
<div class="paragraph">
<p>Without knowing prior to that, this was exactly what I need, so well done marketing:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Seems to be <a href="https://itsfoss.com/what-is-foss/">FOSS</a></p>
</li>
<li>
<p>Single solution to address the <a href="https://www.crowdstrike.com/cybersecurity-101/observability/three-pillars-of-observability/">three pillars</a></p>
</li>
<li>
<p>Nice and complete package</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>That sounds rather too good, but time to put on my wizard hat and to check the brief.
Before messing with <a href="https://www.docker.com/">Docker</a>, I checked the documentation and discovered an architecture
overview and this looks like they hold their part of the bargain:</p>
</div>
<p><object data='/uml/dd869f65209a6159a9836e898b2aed66.svg' type='image/svg+xml'  class='plantuml'></object></p>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Apps can directly send data to SigNoz</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td><a href="https://opentelemetry.io/docs/collector/">Otel collectors</a> can transmit data as well</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Internally another custom collector provides the endpoints to receive data</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>Though I haven&#8217;t heared of <a href="https://clickhouse.com/">Clickhouse</a> either before, but columnar storage sounds about right</td>
</tr>
<tr>
<td><i class="conum" data-value="5"></i><b>5</b></td>
<td>Some abstraction to query the actual data</td>
</tr>
<tr>
<td><i class="conum" data-value="6"></i><b>6</b></td>
<td><a href="https://prometheus.io/docs/alerting/latest/alertmanager/">Alert Manager</a> keeps tracks and handles all the various alerts - glad they haven&#8217;t reinvented the wheel</td>
</tr>
<tr>
<td><i class="conum" data-value="7"></i><b>7</b></td>
<td>And the shiny bit we&#8217;ve spoken of before</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="collecting-data">Collecting data</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Once Signoz is running, which basically boils down to calling <a href="https://docs.docker.com/compose/">docker-compose</a>, a nice starter
question is how to deliver your actual data to it.</p>
</div>
<div class="paragraph">
<p><a href="https://opentelemetry.io/">OpenTelemetry</a> is the defacto standard for that and offers many ways to gather, collect and
transmit data via highly configurable <a href="https://opentelemetry.io/docs/collector/configuration/">pipelines</a>.
The only noteworthy thing here to pay attention to the size of the generated logs - which may cause
some headaches as it did for me during my vacation.</p>
</div>
<div class="paragraph">
<p>While playing with SigNoz I discovered it doesn&#8217;t connect each of its containers separately to an
<a href="https://opentelemetry.io/docs/collector/">OpenTelemetry Collector</a>.<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup>, but passes this task entirely to a
container with <a href="https://github.com/gliderlabs/logspout">logspout</a>.</p>
</div>
<div class="paragraph">
<p>After a quick glance at the <a href="https://github.com/">Github</a> page marketing did its thing again:</p>
</div>
<div class="quoteblock">
<blockquote>
<div class="paragraph">
<p>Logspout is a log router for Docker containers that runs inside Docker. It attaches to all
containers on a host, then routes their logs wherever you want. It also has an extensible module
system.</p>
</div>
</blockquote>
<div class="attribution">
&#8212; https://github.com/gliderlabs/logspout
</div>
</div>
<div class="paragraph">
<p>Alright, this still sounds like a splendid idea and is exactly we do in the example.
In fact, there isn&#8217;t much we have to configure at all:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><a href="https://www.docker.com/">Docker</a> needs a minimal config to get us started:</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="yaml">  <span class="na">logspout</span><span class="pi">:</span>
    <span class="na">container_name</span><span class="pi">:</span> <span class="s">todo-logspout</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s2">"</span><span class="s">docker.io/gliderlabs/logspout:latest"</span>
    <span class="na">pull_policy</span><span class="pi">:</span> <span class="s">if_not_present</span>
    <span class="na">volumes</span><span class="pi">:</span> <i class="conum" data-value="1"></i><b>(1)</b>
      <span class="pi">-</span> <span class="s">/etc/hostname:/etc/host_hostname:ro</span>
      <span class="pi">-</span> <span class="s">/var/run/docker.sock:/var/run/docker.sock</span>
    <span class="na">command</span><span class="pi">:</span> <span class="s">syslog+tcp://otelcol:2255</span> <i class="conum" data-value="2"></i><b>(2)</b>
    <span class="na">depends_on</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">otelcol</span>
    <span class="na">restart</span><span class="pi">:</span> <span class="s">on-failure</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Logspout needs access to the Docker socket and hostmapping for convenience</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>This configures a connection to a receiver of our otelcol instance and comes up next</td>
</tr>
</table>
</div>
</li>
<li>
<p>And we have to define a receiver in otelcol:</p>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="yaml"><span class="na">receivers</span><span class="pi">:</span>
  <span class="na">tcplog/docker</span><span class="pi">:</span>
    <span class="na">listen_address</span><span class="pi">:</span> <span class="s2">"</span><span class="s">0.0.0.0:2255"</span>
    <span class="na">operators</span><span class="pi">:</span> <i class="conum" data-value="1"></i><b>(1)</b>
      <span class="pi">-</span> <span class="na">type</span><span class="pi">:</span> <span class="s">regex_parser</span> <i class="conum" data-value="2"></i><b>(2)</b>
        <span class="na">regex</span><span class="pi">:</span> <span class="s1">'</span><span class="s">^&lt;([0-9]+)&gt;[0-9]+</span><span class="nv"> </span><span class="s">(?P&lt;timestamp&gt;[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)</span><span class="nv"> </span><span class="s">(?P&lt;container_id&gt;\S+)</span><span class="nv"> </span><span class="s">(?P&lt;container_name&gt;\S+)</span><span class="nv"> </span><span class="s">[0-9]+</span><span class="nv"> </span><span class="s">-</span><span class="nv"> </span><span class="s">-(</span><span class="nv"> </span><span class="s">(?P&lt;body&gt;.*))?'</span>
        <span class="na">timestamp</span><span class="pi">:</span>
          <span class="na">parse_from</span><span class="pi">:</span> <span class="s">attributes.timestamp</span>
          <span class="na">layout</span><span class="pi">:</span> <span class="s1">'</span><span class="s">%Y-%m-%dT%H:%M:%S.%LZ'</span>
      <span class="pi">-</span> <span class="na">type</span><span class="pi">:</span> <span class="s">move</span> <i class="conum" data-value="3"></i><b>(3)</b>
        <span class="na">from</span><span class="pi">:</span> <span class="s">attributes["body"]</span>
        <span class="na">to</span><span class="pi">:</span> <span class="s">body</span>
      <span class="pi">-</span> <span class="na">type</span><span class="pi">:</span> <span class="s">remove</span>
        <span class="na">field</span><span class="pi">:</span> <span class="s">attributes.timestamp</span>
      <span class="pi">-</span> <span class="na">type</span><span class="pi">:</span> <span class="s">filter</span> <i class="conum" data-value="4"></i><b>(4)</b>
        <span class="na">id</span><span class="pi">:</span> <span class="s">logs_filter</span>
        <span class="na">expr</span><span class="pi">:</span> <span class="s1">'</span><span class="s">attributes.container_name</span><span class="nv"> </span><span class="s">matches</span><span class="nv"> </span><span class="s">"^todo-(postgres|otelcol|logspout)"'</span>
      <span class="pi">-</span> <span class="na">type</span><span class="pi">:</span> <span class="s">json_parser</span>
        <span class="na">parse_form</span><span class="pi">:</span> <span class="s">body</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Operators allow to parse, modify and filter entries</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>This is the default format of the messages logspout forwards to otelcol</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>We basically move our content to the actual body of the entry</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>There might be lots of different containers running, so we limit the entries based on container names</td>
</tr>
</table>
</div>
</li>
</ol>
</div>
</div>
</div>
<div class="sect1">
<h2 id="pillars-in-practice">Pillars in practice</h2>
<div class="sectionbody">
<div class="paragraph">
<p>There is plenty of explanation and definition out there, way better than I can ever provide,
but just to recall the three back to our memory:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 16.6666%;">
<col style="width: 83.3334%;">
</colgroup>
<tbody>
<tr>
<th class="tableblock halign-left valign-top"><p class="tableblock">Logging</p></th>
<td class="tableblock halign-left valign-top"><p class="tableblock">Historical records of system events and errors</p></td>
</tr>
<tr>
<th class="tableblock halign-left valign-top"><p class="tableblock">Tracing</p></th>
<td class="tableblock halign-left valign-top"><p class="tableblock">Visualization of requests flowing through (distributed) systems</p></td>
</tr>
<tr>
<th class="tableblock halign-left valign-top"><p class="tableblock">Metrics</p></th>
<td class="tableblock halign-left valign-top"><p class="tableblock">Numerical data like e.g. performance, response time, memory consumption</p></td>
</tr>
</tbody>
</table>
<div class="sect2">
<h3 id="logging">Logging</h3>
<div class="paragraph">
<p>The first pillar is probably the easiest and there is also lots of help and reasoning out there,
<a href="https://unexist.blog/observability/2022/02/18/logging-vs-tracing.html#logging">including this blog</a>.</p>
</div>
<div class="paragraph">
<p>So best we can do is throw in <a href="https://github.com/rs/zerolog">zerolog</a>, add some handling in a <a href="https://gin-gonic.com/docs/examples/using-middleware/">Gin-gonic middleware</a> and
move on:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="go"><span class="n">logEvent</span><span class="o">.</span><span class="n">Str</span><span class="p">(</span><span class="s">"client_id"</span><span class="p">,</span> <span class="n">param</span><span class="o">.</span><span class="n">ClientIP</span><span class="p">)</span><span class="o">.</span> <i class="conum" data-value="1"></i><b>(1)</b>
    <span class="n">Str</span><span class="p">(</span><span class="s">"correlation_id"</span><span class="p">,</span> <span class="n">correlationId</span><span class="p">)</span><span class="o">.</span> <i class="conum" data-value="2"></i><b>(2)</b>
    <span class="n">Str</span><span class="p">(</span><span class="s">"method"</span><span class="p">,</span> <span class="n">param</span><span class="o">.</span><span class="n">Method</span><span class="p">)</span><span class="o">.</span>
    <span class="n">Int</span><span class="p">(</span><span class="s">"status_code"</span><span class="p">,</span> <span class="n">param</span><span class="o">.</span><span class="n">StatusCode</span><span class="p">)</span><span class="o">.</span>
    <span class="n">Int</span><span class="p">(</span><span class="s">"body_size"</span><span class="p">,</span> <span class="n">param</span><span class="o">.</span><span class="n">BodySize</span><span class="p">)</span><span class="o">.</span>
    <span class="n">Str</span><span class="p">(</span><span class="s">"path"</span><span class="p">,</span> <span class="n">param</span><span class="o">.</span><span class="n">Path</span><span class="p">)</span><span class="o">.</span>
    <span class="n">Str</span><span class="p">(</span><span class="s">"latency"</span><span class="p">,</span> <span class="n">param</span><span class="o">.</span><span class="n">Latency</span><span class="o">.</span><span class="n">String</span><span class="p">())</span><span class="o">.</span>
    <span class="n">Msg</span><span class="p">(</span><span class="n">param</span><span class="o">.</span><span class="n">ErrorMessage</span><span class="p">)</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The essential mapping magic happens here</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>A <a href="https://unexist.blog/observability/2022/02/18/logging-vs-tracing.html#correlation-between-messages">correlation id</a>
can help to aggregate log messages of the same origin</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>SigNoz offers lots of different options to search data and if you have any experience with
<a href="https://www.elastic.co/kibana">Kibana</a> and the likes you will probably feel right away at home:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/monitoring_with_signoz/logs.png" alt="logs">
</div>
</div>
<div class="paragraph">
<p>There is also no reason to shy away if you require some kind of aggregation and diagrams with
fancy bars:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/monitoring_with_signoz/logs-aggregate.png" alt="logs aggregate">
</div>
</div>
</div>
<div class="sect2">
<h3 id="tracing">Tracing</h3>
<div class="paragraph">
<p>The second pillar is a slightly different beast and requires special code to enhance and propagate
a trace - this is generally called
<a href="https://unexist.blog/observability/2022/02/18/logging-vs-tracing.html#tracing">instrumentation</a>.</p>
</div>
<div class="paragraph">
<p>OpenTelemetry provides the required toolkit to start a tracer and also add <a href="https://opentelemetry.io/docs/concepts/signals/traces/#spans">spans</a>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="go"><span class="k">func</span> <span class="p">(</span><span class="n">resource</span> <span class="o">*</span><span class="n">TodoResource</span><span class="p">)</span> <span class="n">createTodo</span><span class="p">(</span><span class="n">context</span> <span class="o">*</span><span class="n">gin</span><span class="o">.</span><span class="n">Context</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">tracer</span> <span class="o">:=</span> <span class="n">otel</span><span class="o">.</span><span class="n">GetTracerProvider</span><span class="p">()</span><span class="o">.</span><span class="n">Tracer</span><span class="p">(</span><span class="s">"todo-resource"</span><span class="p">)</span> <i class="conum" data-value="1"></i><b>(1)</b>
    <span class="n">ctx</span><span class="p">,</span> <span class="n">span</span> <span class="o">:=</span> <span class="n">tracer</span><span class="o">.</span><span class="n">Start</span><span class="p">(</span><span class="n">context</span><span class="o">.</span><span class="n">Request</span><span class="o">.</span><span class="n">Context</span><span class="p">(),</span> <span class="s">"create-todo"</span><span class="p">,</span>
        <span class="n">trace</span><span class="o">.</span><span class="n">WithSpanKind</span><span class="p">(</span><span class="n">trace</span><span class="o">.</span><span class="n">SpanKindServer</span><span class="p">))</span>
    <span class="k">defer</span> <span class="n">span</span><span class="o">.</span><span class="n">End</span><span class="p">()</span>

    <span class="k">var</span> <span class="n">todo</span> <span class="n">domain</span><span class="o">.</span><span class="n">Todo</span>

    <span class="k">if</span> <span class="no">nil</span> <span class="o">==</span> <span class="n">context</span><span class="o">.</span><span class="n">Bind</span><span class="p">(</span><span class="o">&amp;</span><span class="n">todo</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">var</span> <span class="n">err</span> <span class="kt">error</span>

        <span class="c">// Fetch id</span>
        <span class="n">todo</span><span class="o">.</span><span class="n">UUID</span><span class="p">,</span> <span class="n">err</span> <span class="o">=</span> <span class="n">resource</span><span class="o">.</span><span class="n">idService</span><span class="o">.</span><span class="n">GetId</span><span class="p">(</span><span class="n">ctx</span><span class="p">)</span>

        <span class="k">if</span> <span class="no">nil</span> <span class="o">!=</span> <span class="n">err</span> <span class="p">{</span>
            <span class="n">context</span><span class="o">.</span><span class="n">JSON</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">StatusBadRequest</span><span class="p">,</span> <span class="n">gin</span><span class="o">.</span><span class="n">H</span><span class="p">{</span><span class="s">"error"</span><span class="o">:</span> <span class="n">err</span><span class="o">.</span><span class="n">Error</span><span class="p">()})</span>

            <span class="n">span</span><span class="o">.</span><span class="n">SetStatus</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">StatusBadRequest</span><span class="p">,</span> <span class="s">"UUID failed"</span><span class="p">)</span> <i class="conum" data-value="2"></i><b>(2)</b>
            <span class="n">span</span><span class="o">.</span><span class="n">RecordError</span><span class="p">(</span><span class="n">err</span><span class="p">)</span> <i class="conum" data-value="3"></i><b>(3)</b>

            <span class="k">return</span>
        <span class="p">}</span>

        <span class="c">// Create todo</span>
        <span class="k">if</span> <span class="n">err</span> <span class="o">=</span> <span class="n">resource</span><span class="o">.</span><span class="n">todoService</span><span class="o">.</span><span class="n">CreateTodo</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">todo</span><span class="p">);</span> <span class="no">nil</span> <span class="o">!=</span> <span class="n">err</span> <span class="p">{</span>
            <span class="n">context</span><span class="o">.</span><span class="n">JSON</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">StatusBadRequest</span><span class="p">,</span> <span class="n">gin</span><span class="o">.</span><span class="n">H</span><span class="p">{</span><span class="s">"error"</span><span class="o">:</span> <span class="n">err</span><span class="o">.</span><span class="n">Error</span><span class="p">()})</span>

            <span class="k">return</span>
        <span class="p">}</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="n">context</span><span class="o">.</span><span class="n">JSON</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">StatusBadRequest</span><span class="p">,</span> <span class="s">"Invalid request payload"</span><span class="p">)</span>

        <span class="k">return</span>
    <span class="p">}</span>

    <span class="n">span</span><span class="o">.</span><span class="n">SetStatus</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">StatusCreated</span><span class="p">,</span> <span class="s">"Todo created"</span><span class="p">)</span>
    <span class="n">span</span><span class="o">.</span><span class="n">SetAttributes</span><span class="p">(</span><span class="n">attribute</span><span class="o">.</span><span class="n">Int</span><span class="p">(</span><span class="s">"id"</span><span class="p">,</span> <span class="n">todo</span><span class="o">.</span><span class="n">ID</span><span class="p">),</span> <span class="n">attribute</span><span class="o">.</span><span class="n">String</span><span class="p">(</span><span class="s">"uuid"</span><span class="p">,</span> <span class="n">todo</span><span class="o">.</span><span class="n">UUID</span><span class="p">))</span> <i class="conum" data-value="4"></i><b>(4)</b>

    <span class="n">context</span><span class="o">.</span><span class="n">JSON</span><span class="p">(</span><span class="n">http</span><span class="o">.</span><span class="n">StatusCreated</span><span class="p">,</span> <span class="n">todo</span><span class="p">)</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>This creates a <a href="https://opentelemetry.io/docs/concepts/signals/traces/#tracer">tracer</a> based on the current context</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td><a href="https://opentelemetry.io/docs/concepts/signals/traces/#spans">Spans</a> as working unit of a trace can include a status</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
<td>Error messages can also be thrown in</td>
</tr>
<tr>
<td><i class="conum" data-value="4"></i><b>4</b></td>
<td>And they can also include different types of general <a href="https://opentelemetry.io/docs/concepts/signals/traces/#attributes">span attributes</a></td>
</tr>
</table>
</div>
<div class="paragraph">
<p>The above code calls the <code>id-service</code> and demonstrates how traces can be continued and passed
between service boundaries:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="rouge highlight"><code data-lang="go"><span class="k">func</span> <span class="p">(</span><span class="n">service</span> <span class="o">*</span><span class="n">IdService</span><span class="p">)</span> <span class="n">GetId</span><span class="p">(</span><span class="n">ctx</span> <span class="n">context</span><span class="o">.</span><span class="n">Context</span><span class="p">)</span> <span class="p">(</span><span class="kt">string</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">tracer</span> <span class="o">:=</span> <span class="n">otel</span><span class="o">.</span><span class="n">GetTracerProvider</span><span class="p">()</span><span class="o">.</span><span class="n">Tracer</span><span class="p">(</span><span class="s">"todo-service"</span><span class="p">)</span>
    <span class="n">_</span><span class="p">,</span> <span class="n">span</span> <span class="o">:=</span> <span class="n">tracer</span><span class="o">.</span><span class="n">Start</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="s">"get-id"</span><span class="p">)</span>
    <span class="k">defer</span> <span class="n">span</span><span class="o">.</span><span class="n">End</span><span class="p">()</span>

    <span class="n">response</span><span class="p">,</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">otelhttp</span><span class="o">.</span><span class="n">Get</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"http://%s/id"</span><span class="p">,</span>
        <span class="n">utils</span><span class="o">.</span><span class="n">GetEnvOrDefault</span><span class="p">(</span><span class="s">"APP_ID_HOST_PORT"</span><span class="p">,</span> <span class="s">"localhost:8081"</span><span class="p">)))</span> <i class="conum" data-value="1"></i><b>(1)</b>

    <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
        <span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="n">err</span>
    <span class="p">}</span>

    <span class="n">jsonBytes</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">io</span><span class="o">.</span><span class="n">ReadAll</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">Body</span><span class="p">)</span>

    <span class="k">var</span> <span class="n">reply</span> <span class="n">IdServiceReply</span>

    <span class="n">err</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">Unmarshal</span><span class="p">(</span><span class="n">jsonBytes</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">reply</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
        <span class="k">return</span> <span class="s">""</span><span class="p">,</span> <span class="n">err</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">reply</span><span class="o">.</span><span class="n">UUID</span><span class="p">,</span> <span class="no">nil</span>
<span class="p">}</span></code></pre>
</div>
</div>
<div class="colist arabic">
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>The <a href="https://pkg.go.dev/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp">otelhttp</a> package makes it really easy to propagate traces</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>When everything is set up correctly propagated traces look like this:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/monitoring_with_signoz/traces.png" alt="traces">
</div>
</div>
</div>
<div class="sect2">
<h3 id="metrics">Metrics</h3>
<div class="paragraph">
<p>The last pillar is one of the most interesting and probably the most troublesome, since there
is no easy recipe what could and what should be done.</p>
</div>
<div class="paragraph">
<p>Metrics can generally be of following <a href="https://prometheus.io/docs/concepts/metric_types/">types</a>:</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 16.6666%;">
<col style="width: 83.3334%;">
</colgroup>
<tbody>
<tr>
<th class="tableblock halign-left valign-top"><p class="tableblock">Counter</p></th>
<td class="tableblock halign-left valign-top"><p class="tableblock">A simple monotonically increasing counter which can be reset</p></td>
</tr>
<tr>
<th class="tableblock halign-left valign-top"><p class="tableblock">Gauge</p></th>
<td class="tableblock halign-left valign-top"><p class="tableblock">A single value that can go arbitrarily up and down</p></td>
</tr>
<tr>
<th class="tableblock halign-left valign-top"><p class="tableblock">Histogram</p></th>
<td class="tableblock halign-left valign-top"><p class="tableblock">A time series of counter values and a sum</p></td>
</tr>
<tr>
<th class="tableblock halign-left valign-top"><p class="tableblock">Summary</p></th>
<td class="tableblock halign-left valign-top"><p class="tableblock">A histogram with a sum and quantile over a sliding window</p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
<p>This allows a broad range of measurements like the count of requests or the avg latency between
each of them and has to be figured out for each service or rather service landscape individually.</p>
</div>
<div class="paragraph">
<p>Still, when there are metrics they can be displayed on dashboards like this:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/monitoring_with_signoz/metrics.png" alt="metrics">
</div>
</div>
</div>
<div class="sect2">
<h3 id="alerts">Alerts</h3>
<div class="paragraph">
<p>Although not directly related to the three pillars, <a href="https://medium.com/@letathenasleep/alerting-the-dos-and-don-ts-for-effective-observability-139db9fb49d1">alerts</a> are a nice mechanic to define
thresholds and intervals to receive notification over various kind of channels.</p>
</div>
<div class="paragraph">
<p>The <a href="https://signoz.io/docs/userguide/alerts-management/">documentation</a> is as usual quite nice and there isn&#8217;t much to add here, besides the fact
a paid subscription is required to connect SigNoz to teams.
There is also a way to fallback to <a href="https://make.powerautomate.com/">Power Automate</a>, unfortunately this requires another
subscription.</p>
</div>
<div class="paragraph">
<p>A little hack is to use connectors for <a href="https://prometheus.io/">Prometheus</a>, but please consider supporting the good work of the
folks of SigNoz:</p>
</div>
<div class="paragraph">
<p><a href="https://github.com/prometheus-msteams/prometheus-msteams" class="bare">https://github.com/prometheus-msteams/prometheus-msteams</a></p>
</div>
<div class="imageblock">
<div class="content">
<img src="/assets/images/monitoring_with_signoz/alerts.png" alt="alerts">
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>SigNoz is a great alternative to the established different solutions like EFK or <a href="https://grafana.com/">Grafana</a> in a
well-rounded package.
It is easy to install and so far as I can say easy to maintain and definitely worth a try.</p>
</div>
<div class="paragraph">
<p>All examples can be found here:</p>
</div>
<div class="paragraph">
<p><a href="https://github.com/unexist/showcase-signoz-golang" class="bare">https://github.com/unexist/showcase-signoz-golang</a></p>
</div>
</div>
</div>
<div id="footnotes">
<hr>
<div class="footnote" id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. otelcol in short
</div>
</div>]]></content><author><name>Christoph Kappel</name></author><category term="observability" /><category term="golang" /><category term="tracing" /><category term="opentelemetry" /><category term="logging" /><category term="prometheus" /><category term="logsprout" /><category term="alertmanager" /><category term="signoz" /><category term="showcase" /><summary type="html"><![CDATA[This blog post summarizes observability, briefly explains the missing pillar from a previous post and introduces the monitoring platform SigNoz.]]></summary></entry></feed>