<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ali Amer</title>
    <description>The latest articles on DEV Community by Ali Amer (@aliamerj).</description>
    <link>https://dev.to/aliamerj</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1131589%2Ff613289d-aa12-47bd-b70d-734bf3c1ab9d.jpeg</url>
      <title>DEV Community: Ali Amer</title>
      <link>https://dev.to/aliamerj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aliamerj"/>
    <language>en</language>
    <item>
      <title>Ep2: Working on logger for Nusku</title>
      <dc:creator>Ali Amer</dc:creator>
      <pubDate>Tue, 06 Oct 2026 14:21:00 +0000</pubDate>
      <link>https://dev.to/aliamerj/ep2-working-on-logger-for-nusku-46c2</link>
      <guid>https://dev.to/aliamerj/ep2-working-on-logger-for-nusku-46c2</guid>
      <description>&lt;p&gt;Since we are using Zig and the available libraries and packages are limited, we need to implement some of the core infrastructure code that we need for this project.&lt;/p&gt;

&lt;p&gt;On the first day, we implemented a simple, lightweight CLI framework to help with parsing arguments and interacting with the CLI.&lt;/p&gt;

&lt;p&gt;Today, we are starting to work on something new: the logger module. Like any logging library, you write a message along with some named fields. The module renders one line per record as readable text, logfmt, or JSON, with a timestamp and a log level, and writes it to a writer that you provide, such as stderr for a CLI application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick start
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;main&lt;/code&gt;, create one logger and install it as the default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;@import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"log"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;err_buf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;u8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;io&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;err_buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;io&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;io&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shouldColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;io&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;environ_map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NO_COLOR"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clearDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then log from anywhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"server started"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7000&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c"&gt;// or give a file or component its own scope name:&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"agent"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sample taken"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;took_us&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"write failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;cause&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;AccessDenied&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2026-10-05T08:08:02.803Z INFO  server started addr=0.0.0.0 port=7000
2026-10-05T08:08:02.803Z DEBUG agent: sample taken pid=412 took_us=88
2026-10-05T08:08:02.804Z ERROR agent: write failed path=/x cause=AccessDenied
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Output formats
&lt;/h2&gt;

&lt;p&gt;Set with &lt;code&gt;Options.format&lt;/code&gt; or at run time with &lt;code&gt;logger.setFormat(...)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;.text&lt;/code&gt;&lt;/strong&gt; (default). For people reading a terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2026-10-05T14:03:22.123Z INFO  agent: listening addr=:7000 interval_ms=100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Level is padded to five characters so columns line up. With &lt;code&gt;color = true&lt;/code&gt; the level is colored (trace gray, debug cyan, info green, warn yellow, error red), the timestamp and field names are dimmed. Newlines in a message are shown as &lt;code&gt;\n&lt;/code&gt; so one record is always one line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;.logfmt&lt;/code&gt;&lt;/strong&gt;. For grep and log tools that understand &lt;code&gt;key=value&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;time=2026-10-05T14:03:22.123Z level=info scope=agent msg=listening addr=:7000 interval_ms=100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;.json&lt;/code&gt;&lt;/strong&gt;. One object per line, for log pipelines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2026-10-05T14:03:22.123Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"info"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"msg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"listening"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"addr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;":7000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"interval_ms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Color is only ever applied to &lt;code&gt;.text&lt;/code&gt;. The other two never contain escape codes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Levels
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;trace &amp;lt; debug &amp;lt; info &amp;lt; warn &amp;lt; err &amp;lt; off&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A record is written when its level is at or above the logger's threshold. &lt;code&gt;off&lt;/code&gt; as a threshold silences everything. The default threshold is &lt;code&gt;info&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLevel&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="py"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;          &lt;span class="c"&gt;// any thread, any time&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLevel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;(.&lt;/span&gt;&lt;span class="py"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;           &lt;span class="c"&gt;// false at the moment&lt;/span&gt;

&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"WARNING"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c"&gt;// ?Level: case-insensitive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Level.parse&lt;/code&gt; accepts &lt;code&gt;trace&lt;/code&gt;, &lt;code&gt;debug&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, &lt;code&gt;warning&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, &lt;code&gt;err&lt;/code&gt;, &lt;code&gt;off&lt;/code&gt;, &lt;code&gt;none&lt;/code&gt;. It is meant for flags and environment variables.&lt;/p&gt;

&lt;p&gt;The level check happens before any formatting or locking, so a disabled call costs one atomic load. The field values you pass are still evaluated by the caller, so avoid building expensive values just to log them at debug level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fields
&lt;/h2&gt;

&lt;p&gt;The second argument is an anonymous struct. Each field becomes a key and a value, in the order written.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;took_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;12.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Supported value types and how they print:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;integers, comptime ints&lt;/td&gt;
&lt;td&gt;decimal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;floats&lt;/td&gt;
&lt;td&gt;decimal. In JSON, NaN and infinity become &lt;code&gt;null&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;true&lt;/code&gt; / &lt;code&gt;false&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;[]const u8&lt;/code&gt;, string literals, &lt;code&gt;[N]u8&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;text. In text and logfmt it is quoted only when needed (empty, or contains a space, &lt;code&gt;=&lt;/code&gt;, &lt;code&gt;"&lt;/code&gt;, &lt;code&gt;\&lt;/code&gt;, or a control character). In JSON it is always quoted and escaped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;enums and enum literals&lt;/td&gt;
&lt;td&gt;the tag name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;error values&lt;/td&gt;
&lt;td&gt;the error name, &lt;code&gt;cause=AccessDenied&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;optionals&lt;/td&gt;
&lt;td&gt;the value, or &lt;code&gt;null&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;anything else&lt;/td&gt;
&lt;td&gt;its &lt;code&gt;{any}&lt;/code&gt; rendering as a string, cut at 512 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Logging never fails the caller. If the writer errors (a closed pipe, a full disk) the record is dropped silently.&lt;/p&gt;

&lt;p&gt;An empty &lt;code&gt;.{}&lt;/code&gt; is fine when you have no fields. A struct with positional fields (a tuple like &lt;code&gt;.{ 1, 2 }&lt;/code&gt;) is a compile error, since fields need names.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scopes and bound context
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;scope&lt;/strong&gt; is a name for the part of the program that produced the record. It prints as &lt;code&gt;agent:&lt;/code&gt; in text and as &lt;code&gt;scope=agent&lt;/code&gt; or &lt;code&gt;"scope":"agent"&lt;/code&gt; in the others.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;bound&lt;/strong&gt; handle carries a scope and fixed fields, so you do not repeat them on every call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="c"&gt;// attached to a specific logger&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;request_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"handled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c"&gt;// -&amp;gt; INFO  http: handled request_id=abc status=200&lt;/span&gt;

&lt;span class="c"&gt;// chain more fields; earlier ones print first&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;user_req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ali"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c"&gt;// same fields, different scope name&lt;/span&gt;
&lt;span class="n"&gt;user_req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scoped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http.slow"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"slow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1800&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Handles are small values. Build them on the stack, pass them around, copy them freely. They hold a pointer to the logger and no allocations, so they are only valid while the logger is.&lt;/p&gt;

&lt;p&gt;The module-level &lt;code&gt;log.scope(name)&lt;/code&gt; and &lt;code&gt;log.with(fields)&lt;/code&gt; return handles that are not attached to a logger. They look up the default logger every time you log. That is what allows a file-level constant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;slog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"db"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c"&gt;// at file level, before main runs&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"running"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{});&lt;/span&gt;  &lt;span class="c"&gt;// uses whatever default is installed now&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The default logger
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;log.setDefault(&amp;amp;logger)&lt;/code&gt; installs a process-wide logger. The functions &lt;code&gt;log.info(...)&lt;/code&gt;, &lt;code&gt;log.debug(...)&lt;/code&gt; and so on, and every unattached handle, use it. &lt;code&gt;log.clearDefault()&lt;/code&gt; removes it, and &lt;code&gt;log.getDefault()&lt;/code&gt; returns &lt;code&gt;?*Logger&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If no default is installed, these calls do nothing. They do not crash and they do not print. This means anything logged before &lt;code&gt;main&lt;/code&gt; calls &lt;code&gt;setDefault&lt;/code&gt; is lost, so install it first.&lt;/p&gt;

&lt;p&gt;The default is held in an atomic pointer, so installing and reading it is thread safe. The logger itself must stay alive while it is installed. In &lt;code&gt;main&lt;/code&gt;, &lt;code&gt;defer log.clearDefault()&lt;/code&gt; right after &lt;code&gt;setDefault&lt;/code&gt; covers it.&lt;/p&gt;

&lt;p&gt;If you prefer no global at all, ignore the module-level functions and pass &lt;code&gt;*Logger&lt;/code&gt; or a bound handle explicitly. Nothing else needs the global.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it with the cli module
&lt;/h2&gt;

&lt;p&gt;The two modules are independent. The app connects them in a small file, &lt;code&gt;src/logging.zig&lt;/code&gt;, which defines the flags and applies them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;cli&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Flag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"verbose"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;short&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'v'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;help&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Same as --log-level=debug"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"log-level"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;help&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"trace, debug, info, warn, error, off"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"PCTL_LOG_LEVEL"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"log-format"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;help&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"text, logfmt or json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"PCTL_LOG_FORMAT"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cli&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;error&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InvalidUsage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The root command lists &lt;code&gt;logging.flags&lt;/code&gt; as persistent flags, and each command's &lt;code&gt;run&lt;/code&gt; calls &lt;code&gt;try logging.configure(ctx)&lt;/code&gt; first. A bad value like &lt;code&gt;--log-level=loud&lt;/code&gt; becomes a usage error: exit 2 with a hint.&lt;/p&gt;

&lt;p&gt;Two rules keep this tidy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Share one stderr writer.&lt;/strong&gt; &lt;code&gt;main&lt;/code&gt; passes the same &lt;code&gt;&amp;amp;err.interface&lt;/code&gt; to &lt;code&gt;cli.execute&lt;/code&gt; and to the logger. If they used separate buffered writers, their output could appear out of order. Because the logger flushes after every record, ordering stays correct.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logs are not results.&lt;/strong&gt; Command output goes to &lt;code&gt;ctx.stdout&lt;/code&gt;. Logging goes to stderr. So &lt;code&gt;pctl query ... | jq&lt;/code&gt; is unaffected by &lt;code&gt;-v&lt;/code&gt; or &lt;code&gt;--log-format json&lt;/code&gt;.
Because &lt;code&gt;cli&lt;/code&gt; has no pre-run hook yet, every command calls &lt;code&gt;configure&lt;/code&gt;. A &lt;code&gt;persistent_pre_run&lt;/code&gt; hook on &lt;code&gt;Command&lt;/code&gt; would remove that repetition and is a good next change to &lt;code&gt;cli&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Routing &lt;code&gt;std.log&lt;/code&gt; into it
&lt;/h2&gt;

&lt;p&gt;Libraries and the standard library log through &lt;code&gt;std.log&lt;/code&gt;. To send those records through the same logger, set this in your root source file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std_options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;logFn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;stdLogFn&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Levels map one to one (&lt;code&gt;err&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;debug&lt;/code&gt;). The &lt;code&gt;std.log&lt;/code&gt; scope becomes the scope name, and the default scope prints no scope. The message is formatted into a 1 KiB buffer and cut if longer. Since &lt;code&gt;std.log&lt;/code&gt; has no structured fields, those records carry only a message.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Files&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;root.zig&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Public API and the module-level convenience functions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;logger.zig&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Logger&lt;/code&gt;, &lt;code&gt;Bound&lt;/code&gt;, the default-logger pointer, &lt;code&gt;shouldColor&lt;/code&gt;. Owns the lock and the clock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;encode.zig&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Turns one record into bytes in any of the three formats. Pure: it only touches the writer it is given.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;level.zig&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Level&lt;/code&gt;, names, labels, &lt;code&gt;parse&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;time.zig&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;UTC timestamp formatting with no allocation.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What happens on a call&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;emit&lt;/code&gt; compares the level against the atomic threshold and returns if disabled.&lt;/li&gt;
&lt;li&gt;It takes the logger's mutex (&lt;code&gt;std.Io.Mutex&lt;/code&gt;). This is why two threads can never mix their records.&lt;/li&gt;
&lt;li&gt;It reads the clock (&lt;code&gt;std.Io.Clock.real&lt;/code&gt;, or &lt;code&gt;now_fn&lt;/code&gt; if you set one) and formats the timestamp.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;encode.writeRecord&lt;/code&gt; writes the whole line straight into the writer. There is no intermediate buffer and no allocation, so there is no length limit on a record.&lt;/li&gt;
&lt;li&gt;It flushes the writer if &lt;code&gt;flush&lt;/code&gt; is on (the default), then unlocks.
&lt;strong&gt;How fields are encoded without allocating.&lt;/strong&gt; Fields are an anonymous struct, so their names and types are known at compile time. &lt;code&gt;writeFields&lt;/code&gt; walks them with &lt;code&gt;inline for&lt;/code&gt;, and each value is handled by a &lt;code&gt;switch&lt;/code&gt; on its type, resolved at compile time. Nothing is boxed or stored.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Fixed fields on a bound handle are joined to per-call fields with a small generic type, &lt;code&gt;Pair(A, B)&lt;/code&gt;, which just holds both. &lt;code&gt;writeFields&lt;/code&gt; recognizes it and writes &lt;code&gt;a&lt;/code&gt; then &lt;code&gt;b&lt;/code&gt;. Chaining &lt;code&gt;.with&lt;/code&gt; nests pairs, and the order stays first-bound first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Settings that change at run time.&lt;/strong&gt; The level is an atomic value. Format and color are read under the mutex, so &lt;code&gt;setFormat&lt;/code&gt; and &lt;code&gt;setColor&lt;/code&gt; lock it too. All three are safe to call while other threads are logging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits and known gaps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No file output, rotation, or network sinks.&lt;/strong&gt; It writes to one writer. For a CLI that is stderr, and a service can redirect that to a file or a collector.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One output per logger.&lt;/strong&gt; To write text to the terminal and JSON to a file, you would create two loggers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Field values are evaluated by the caller.&lt;/strong&gt; A disabled level skips formatting but not the work of building the arguments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fields only on the structured API.&lt;/strong&gt; &lt;code&gt;std.log&lt;/code&gt; records have a message and a scope but no fields.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Records before &lt;code&gt;setDefault&lt;/code&gt; are dropped.&lt;/strong&gt; There is no buffering of early logs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The lock is held while writing.&lt;/strong&gt; A slow writer blocks other threads that log. For stderr that is normally fine. If it becomes a problem, put a buffered writer in front.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No sampling or rate limiting&lt;/strong&gt;, and no compile-time level floor to strip debug calls from release builds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Errors are logged by name.&lt;/strong&gt; The error name is printed, with no error return trace.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://nuskum.github.io/nusku/" rel="noopener noreferrer"&gt;https://nuskum.github.io/nusku/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>zig</category>
      <category>linux</category>
      <category>performance</category>
      <category>systems</category>
    </item>
    <item>
      <title>Ep1: Setting Up The Project</title>
      <dc:creator>Ali Amer</dc:creator>
      <pubDate>Mon, 05 Oct 2026 11:24:37 +0000</pubDate>
      <link>https://dev.to/aliamerj/ep1-setting-up-the-project-n7o</link>
      <guid>https://dev.to/aliamerj/ep1-setting-up-the-project-n7o</guid>
      <description>&lt;p&gt;This is first task, we should do is building the infrastructure for the tool and the most important one is Cli&lt;/p&gt;

&lt;h1&gt;
  
  
  The cli module
&lt;/h1&gt;

&lt;p&gt;A small command-line framework for Zig 0.16, modeled on Go's cobra. You describe your commands as a tree of constant structs. The module parses argv, picks the right command, fills in flag values, prints help, and turns errors into the right stream and exit code.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;For an app with many commands and subcommands, it handles the parts that are the same every time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resolving &lt;code&gt;nusku agent start --listen :7000&lt;/code&gt; to the &lt;code&gt;start&lt;/code&gt; command under &lt;code&gt;agent&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Parsing flags: &lt;code&gt;--flag value&lt;/code&gt;, &lt;code&gt;--flag=value&lt;/code&gt;, &lt;code&gt;-f value&lt;/code&gt;, &lt;code&gt;-fvalue&lt;/code&gt;, bool clusters like &lt;code&gt;-abc&lt;/code&gt;, &lt;code&gt;--no-flag&lt;/code&gt;, and &lt;code&gt;--&lt;/code&gt; to end flag parsing.&lt;/li&gt;
&lt;li&gt;Flag values from three places, in this order: command line, environment variable, default.&lt;/li&gt;
&lt;li&gt;Checking types (int, bool), required flags, and the number of positional arguments, before your code runs.&lt;/li&gt;
&lt;li&gt;Generating &lt;code&gt;--help&lt;/code&gt; for every command from the tree.&lt;/li&gt;
&lt;li&gt;Keeping Unix stream rules: results on stdout, errors and progress on stderr, and meaningful exit codes.
Your command code only has to do its job and return.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick start
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;build.zig&lt;/code&gt;: register the module and give it to the app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;cli_mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cli"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;root_source_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cli/root.zig"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;optimize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;optimize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c"&gt;// Nusku commands&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;commands_mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"commands"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;root_source_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"src/cmd/root.zig"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;optimize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;optimize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;imports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"cli"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cli_mod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;exe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addExecutable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"nusku"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;root_module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;root_source_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"src/main.zig"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;optimize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;optimize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;imports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"commands"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;commands_mod&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"cli"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;module&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cli_mod&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  File map
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;root.zig&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Public API. Re-exports the types and functions you use, and pulls in every file's tests.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;command.zig&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Plain data types: &lt;code&gt;Command&lt;/code&gt;, &lt;code&gt;Flag&lt;/code&gt;, &lt;code&gt;ArgsRule&lt;/code&gt;, &lt;code&gt;FlagValues&lt;/code&gt;, &lt;code&gt;Context&lt;/code&gt;. No parsing logic.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;parser.zig&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Turns &lt;code&gt;argv&lt;/code&gt; into a &lt;code&gt;Parsed&lt;/code&gt; result or a parse error with a message. Never prints.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;help.zig&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Renders help text and the usage hint. Pure functions over the tree.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;errors.zig&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The two special error names and the exit code constants.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;execute.zig&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The entry point. Runs the parser, dispatches to &lt;code&gt;run&lt;/code&gt;, maps outcomes to streams and exit codes.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The data model
&lt;/h2&gt;

&lt;p&gt;Everything in &lt;code&gt;command.zig&lt;/code&gt; is plain data. Commands are &lt;code&gt;const&lt;/code&gt; values linked with pointers, so a whole tree can sit in static memory with no allocation.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Command&lt;/code&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The word the user types.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aliases&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Other words that select the same command (&lt;code&gt;ls&lt;/code&gt; for &lt;code&gt;list&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;short&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One line, shown in the parent's command list.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;long&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Longer text shown at the top of this command's own help. Falls back to &lt;code&gt;short&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;usage_args&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Positional synopsis for the usage line, like &lt;code&gt;&amp;lt;file&amp;gt;...&lt;/code&gt;. Display only.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;flags&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Local flags. Visible only on this command.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;persistent_flags&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Visible on this command and all its descendants.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;subcommands&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;[]const *const Command&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;args&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rule for the number of positional arguments (see below).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hidden&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Left out of the parent's command list. Still runnable.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;run&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;?RunFn&lt;/code&gt;. Null makes it a grouping command that prints help when invoked.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Flag&lt;/code&gt;
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Long name without dashes. &lt;code&gt;verbose&lt;/code&gt; is &lt;code&gt;--verbose&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;short&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Optional one-character alias, like &lt;code&gt;'v'&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kind&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.bool&lt;/code&gt;, &lt;code&gt;.string&lt;/code&gt;, or &lt;code&gt;.int&lt;/code&gt;. Default &lt;code&gt;.string&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;help&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One line for the help output.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;default&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Text default. If null: &lt;code&gt;false&lt;/code&gt;, empty string, or &lt;code&gt;0&lt;/code&gt; depending on &lt;code&gt;kind&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;env&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Environment variable to read when the flag is not on the command line.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;required&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Usage error if neither the command line nor the env supplies it.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Defaults are written as text (&lt;code&gt;"50"&lt;/code&gt;, &lt;code&gt;":7000"&lt;/code&gt;) because they go through the same validation as user input.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;ArgsRule&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Controls how many positional arguments are allowed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;any&lt;/span&gt;                                  &lt;span class="c"&gt;// no check (default)&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;none&lt;/span&gt;                                 &lt;span class="c"&gt;// exactly zero&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;min&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;range&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;min&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;FlagValues&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The result of flag resolution. It maps each flag name to an &lt;code&gt;Entry&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="n"&gt;Entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ValueSource&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;   &lt;span class="c"&gt;// source: .default, .env, .cli&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every flag visible to the chosen command gets an entry, with its default filled in. That is why the getters never return null:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"verbose"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c"&gt;// true if the user supplied it by CLI or env, false for the default&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Asking for a flag name that is not defined for the command is a programmer error and panics with a clear message.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;Context&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;What &lt;code&gt;run&lt;/code&gt; receives.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allocator&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An arena that lives for this one invocation. Do not free what you allocate from it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;prog&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name of the root command.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stdout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;*std.Io.Writer&lt;/code&gt;. Results only.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;stderr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;*std.Io.Writer&lt;/code&gt;. Diagnostics, progress, warnings.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;command&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The command being run.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;args&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Positional arguments, with command names and flags already removed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;flags&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The resolved &lt;code&gt;FlagValues&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Plus the helper methods &lt;code&gt;getBool&lt;/code&gt;, &lt;code&gt;getString&lt;/code&gt;, &lt;code&gt;getInt&lt;/code&gt;, &lt;code&gt;isSet&lt;/code&gt;, &lt;code&gt;fail&lt;/code&gt;, and &lt;code&gt;usageFail&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How parsing works
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;parser.parse(arena, root, argv, opts, &amp;amp;diag)&lt;/code&gt; makes one pass over the tokens, then finishes with a short validation step.&lt;/p&gt;

&lt;h3&gt;
  
  
  The pass
&lt;/h3&gt;

&lt;p&gt;The parser keeps a stack called &lt;code&gt;path&lt;/code&gt;, which starts as &lt;code&gt;[root]&lt;/code&gt;. The last item is the current command. For each token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;token
 |
 |- after "--"  ........................ positional
 |- does not start with "-" (or is just "-") ...
 |      |- no positional seen yet and current command has a subcommand with this name
 |      |        -&amp;gt; push that subcommand onto path
 |      |- no positional yet, current command has subcommands but no `run`
 |      |        -&amp;gt; error UnknownCommand
 |      '- otherwise -&amp;gt; positional
 |- exactly "--"  ...................... switch to "everything is positional"
 |- starts with "--"  .................. long flag
 '- starts with "-"  ................... short flag(s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key point is that flags are looked up against whichever command is current at that moment. This gives cobra's behavior:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nusku --verbose query -n5 x      ok: verbose is persistent on the root
nusku query -v -n5 x             ok: same flag, after the subcommand
nusku --limit 5 query x          error: limit belongs to query, which is not current yet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Which flags are visible
&lt;/h3&gt;

&lt;p&gt;When looking up a flag name, the parser checks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Local &lt;code&gt;flags&lt;/code&gt; of the current command.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;persistent_flags&lt;/code&gt; of the current command and each ancestor, walking up to the root.
Local flags of ancestors are not visible. If a local flag and a persistent flag share a name, the local one wins on lookup.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Long flags
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;--name=value&lt;/code&gt;, &lt;code&gt;--name value&lt;/code&gt;, &lt;code&gt;--name&lt;/code&gt; (bool). For a bool flag, &lt;code&gt;--name value&lt;/code&gt; does not take &lt;code&gt;value&lt;/code&gt;; the bool is set and &lt;code&gt;value&lt;/code&gt; is parsed as the next token. &lt;code&gt;--no-name&lt;/code&gt; sets a bool flag to false. &lt;code&gt;--help&lt;/code&gt; is recognized if you did not define your own &lt;code&gt;help&lt;/code&gt; flag.&lt;/p&gt;

&lt;h3&gt;
  
  
  Short flags
&lt;/h3&gt;

&lt;p&gt;The token body is scanned left to right:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A bool flag is set and the scan continues, so &lt;code&gt;-abc&lt;/code&gt; sets three flags.&lt;/li&gt;
&lt;li&gt;A flag that takes a value ends the cluster. The value is the rest of the token (&lt;code&gt;-n5&lt;/code&gt;), the part after &lt;code&gt;=&lt;/code&gt; (&lt;code&gt;-n=5&lt;/code&gt;), or the next token (&lt;code&gt;-n 5&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;h&lt;/code&gt; is treated as help if no flag claimed it.
When a flag takes a value from the next token, that token is used as is, even if it starts with &lt;code&gt;-&lt;/code&gt;. So &lt;code&gt;--limit -1&lt;/code&gt; gives the value &lt;code&gt;-1&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Validation at set time
&lt;/h3&gt;

&lt;p&gt;Each value is checked when it is stored. Ints must parse with &lt;code&gt;std.fmt.parseInt(i64, v, 0)&lt;/code&gt;, so &lt;code&gt;0x10&lt;/code&gt; and &lt;code&gt;1_000&lt;/code&gt; style input follows Zig's rules. Bools must be exactly &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;. A bad value is a parse error, so your &lt;code&gt;run&lt;/code&gt; never sees it.&lt;/p&gt;

&lt;h3&gt;
  
  
  After the pass
&lt;/h3&gt;

&lt;p&gt;If help was requested, the parser returns right away with &lt;code&gt;help_requested = true&lt;/code&gt; and skips everything below. This is why &lt;code&gt;nusku query --help&lt;/code&gt; works even though &lt;code&gt;--service&lt;/code&gt; is required.&lt;/p&gt;

&lt;p&gt;Otherwise, for every flag visible to the final command that was not set on the command line:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If the flag has &lt;code&gt;env&lt;/code&gt; and the lookup returns a value, use it (source &lt;code&gt;.env&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Else if the flag is &lt;code&gt;required&lt;/code&gt;, fail with &lt;code&gt;MissingRequiredFlag&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Else store the default (source &lt;code&gt;.default&lt;/code&gt;).
Finally the positional count is checked against the command's &lt;code&gt;ArgsRule&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the precedence is command line, then environment, then default. A required flag can be satisfied by the command line or the environment, but a default never satisfies it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Result and errors
&lt;/h3&gt;

&lt;p&gt;On success, &lt;code&gt;Parsed&lt;/code&gt; holds the chosen &lt;code&gt;command&lt;/code&gt;, the full &lt;code&gt;path&lt;/code&gt; (root to command), &lt;code&gt;args&lt;/code&gt;, &lt;code&gt;flags&lt;/code&gt;, and &lt;code&gt;help_requested&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On failure, &lt;code&gt;parse&lt;/code&gt; returns one of these errors and fills a &lt;code&gt;Diagnostic&lt;/code&gt; with a &lt;code&gt;message&lt;/code&gt;, the failing &lt;code&gt;command&lt;/code&gt;, and its &lt;code&gt;path&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Error&lt;/th&gt;
&lt;th&gt;Example message&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnknownCommand&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;unknown command "lst" for "nusku"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UnknownFlag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;unknown flag: --bogus&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MissingValue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;flag needs a value: --limit&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;InvalidValue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;invalid value "abc" for --limit: expected an integer&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MissingRequiredFlag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;required flag not set: --service&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;InvalidArgCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;get: expects exactly 1 argument(s), got 0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OutOfMemory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The parser never prints. The caller decides how to show the message, which keeps it testable and reusable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment lookup
&lt;/h3&gt;

&lt;p&gt;The parser does not read the process environment itself. It takes an &lt;code&gt;EnvLookup&lt;/code&gt;, a small struct with a context pointer and a function. In a 0.16 &lt;code&gt;main&lt;/code&gt;, &lt;code&gt;cli.EnvLookup.fromMap(init.environ_map)&lt;/code&gt; builds one. In tests you pass a fake. Passing &lt;code&gt;null&lt;/code&gt; turns env fallback off.&lt;/p&gt;

&lt;h2&gt;
  
  
  How help is rendered
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;help.zig&lt;/code&gt; renders from the tree and writes to any &lt;code&gt;*std.Io.Writer&lt;/code&gt;. It does no allocation.&lt;/p&gt;

&lt;p&gt;Sample output for &lt;code&gt;nusku query --help&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Query stored profiles

Usage:
  nusku query [flags] &amp;lt;expression&amp;gt;

Aliases:
  query, q

Flags:
  -h, --help             Show help for this command
  -s, --service string   Service to query [env: NUSKU_SERVICE] (required)
  -n, --limit int        Max rows (default 5)
  -f, --format string    table or json (default "table")
      --reverse          Oldest first

Global Flags:
  -v, --verbose         Print progress to stderr
  -c, --config string   Config file (default "/etc/nusku.toml") [env: NUSKU_CONFIG]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How it is built:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Intro&lt;/strong&gt;: &lt;code&gt;long&lt;/code&gt;, or &lt;code&gt;short&lt;/code&gt; if there is no &lt;code&gt;long&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Usage&lt;/strong&gt;: one line if the command has a &lt;code&gt;run&lt;/code&gt;, another with &lt;code&gt;[command]&lt;/code&gt; if it has visible subcommands.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aliases&lt;/strong&gt;: shown for non-root commands that have any.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commands&lt;/strong&gt;: visible subcommands, names padded to the longest one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flags&lt;/strong&gt;: the command's own &lt;code&gt;flags&lt;/code&gt; plus its own &lt;code&gt;persistent_flags&lt;/code&gt;. An automatic &lt;code&gt;-h, --help&lt;/code&gt; line is added unless you defined a &lt;code&gt;help&lt;/code&gt; flag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Global Flags&lt;/strong&gt;: persistent flags inherited from ancestors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Footer&lt;/strong&gt;: a pointer to &lt;code&gt;nusku [command] --help&lt;/code&gt; when there are subcommands.
Column alignment needs the widest flag label before anything is printed, and there is no allocation to collect the labels into. So each section is walked twice with a small visitor: the first pass measures, the second pass prints with that width. This is why the same traversal function serves both.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;writeUsageHint&lt;/code&gt; prints the shorter line used after errors: &lt;code&gt;Run 'nusku query --help' for usage.&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How &lt;code&gt;execute&lt;/code&gt; ties it together
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;cli.execute(&amp;amp;root, argv, opts)&lt;/code&gt; returns the exit code. It never calls exit itself, so tests can call it and check the result.&lt;/p&gt;

&lt;p&gt;Steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make an arena from &lt;code&gt;opts.allocator&lt;/code&gt;. It is freed when &lt;code&gt;execute&lt;/code&gt; returns.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;opts.version&lt;/code&gt; is set and &lt;code&gt;argv&lt;/code&gt; is exactly &lt;code&gt;["--version"]&lt;/code&gt;, print &lt;code&gt;nusku 0.1.0&lt;/code&gt; to stdout and return 0.&lt;/li&gt;
&lt;li&gt;Call the parser.

&lt;ul&gt;
&lt;li&gt;On a parse error, write &lt;code&gt;nusku: &amp;lt;message&amp;gt;&lt;/code&gt; and the usage hint to stderr, and return 2.&lt;/li&gt;
&lt;li&gt;On out of memory, write a short message and return 1.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If help was requested, or the chosen command has no &lt;code&gt;run&lt;/code&gt;, print its help to stdout and return 0.&lt;/li&gt;
&lt;li&gt;Build a &lt;code&gt;Context&lt;/code&gt; and call &lt;code&gt;run&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Map what &lt;code&gt;run&lt;/code&gt; returns (next section).&lt;/li&gt;
&lt;li&gt;Flush both writers.
## Streams and exit codes&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Stream&lt;/th&gt;
&lt;th&gt;Exit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Normal result&lt;/td&gt;
&lt;td&gt;stdout&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;--help&lt;/code&gt;, &lt;code&gt;--version&lt;/code&gt;, or a grouping command with nothing to run&lt;/td&gt;
&lt;td&gt;stdout&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parse error: unknown command or flag, bad value, missing required flag, wrong argument count&lt;/td&gt;
&lt;td&gt;stderr&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ctx.usageFail(...)&lt;/code&gt; from a command&lt;/td&gt;
&lt;td&gt;stderr&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ctx.fail(...)&lt;/code&gt; from a command&lt;/td&gt;
&lt;td&gt;stderr&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Any other error returned from &lt;code&gt;run&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;stderr, as &lt;code&gt;nusku: error: &amp;lt;ErrorName&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;error.WriteFailed&lt;/code&gt; from a writer (for example `nusku ... \&lt;/td&gt;
&lt;td&gt;head -1`)&lt;/td&gt;
&lt;td&gt;nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two details worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ordering.&lt;/strong&gt; Before writing a diagnostic to stderr, &lt;code&gt;execute&lt;/code&gt; flushes stdout. On a terminal this keeps the two streams in the order things happened.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Broken pipes.&lt;/strong&gt; When the reader of a pipe exits early, writes fail with &lt;code&gt;WriteFailed&lt;/code&gt;. &lt;code&gt;execute&lt;/code&gt; exits quietly instead of printing an error about it.
### The special error names&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In &lt;code&gt;errors.zig&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;error.Reported&lt;/code&gt;: the message is already printed. Returned by &lt;code&gt;ctx.fail&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;error.InvalidUsage&lt;/code&gt;: bad input. Returned by &lt;code&gt;ctx.usageFail&lt;/code&gt;. &lt;code&gt;execute&lt;/code&gt; adds the help hint and exits 2.
Exit codes are constants: &lt;code&gt;exit_success = 0&lt;/code&gt;, &lt;code&gt;exit_failure = 1&lt;/code&gt;, &lt;code&gt;exit_usage = 2&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Writing a command
&lt;/h2&gt;

&lt;p&gt;One file per command keeps a large tree manageable. Each file exports its &lt;code&gt;Command&lt;/code&gt; as a &lt;code&gt;pub const&lt;/code&gt;, and the parent points at it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="c"&gt;// src/commands/query.zig&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;@import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"std"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;cli&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;@import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cli"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cli&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;aliases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"q"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;short&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Query stored profiles"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;usage_args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;expression&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;short&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;required&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"NUSKU_SERVICE"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;short&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"5"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;short&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'f'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"table"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;exact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;run&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cli&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;anyerror&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c"&gt;// input the parser cannot judge: exit 2 with a hint&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"table"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;usageFail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unknown --format &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;{s}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// progress goes to stderr&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getBool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"verbose"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"querying {s}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c"&gt;// the result goes to stdout&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{s}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c"&gt;// an operation that failed: message plus exit 1&lt;/span&gt;
    &lt;span class="c"&gt;// return ctx.fail("cannot open {s}", .{path});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;@import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"commands/query.zig"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cli&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"nusku"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;subcommands&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;cmd&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rules of thumb:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Never write to the process streams directly. Use &lt;code&gt;ctx.stdout&lt;/code&gt; and &lt;code&gt;ctx.stderr&lt;/code&gt;. This is what makes piping correct and what lets tests capture output.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;usageFail&lt;/code&gt; when the user's input is wrong, &lt;code&gt;fail&lt;/code&gt; when the input was fine and the work failed.&lt;/li&gt;
&lt;li&gt;Put flags that apply to many commands in &lt;code&gt;persistent_flags&lt;/code&gt; on a common ancestor.
### Grouping commands&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A command with &lt;code&gt;subcommands&lt;/code&gt; and no &lt;code&gt;run&lt;/code&gt; is a group. Invoking it prints its help and exits 0. A command with both &lt;code&gt;run&lt;/code&gt; and &lt;code&gt;subcommands&lt;/code&gt; is allowed: an unrecognized first word becomes a positional argument instead of an unknown-command error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory and lifetimes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;execute&lt;/code&gt; creates one arena per call and frees it at the end. &lt;code&gt;ctx.allocator&lt;/code&gt; is that arena, so commands can allocate freely and never free.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;parser.parse&lt;/code&gt; allocates from the arena you give it. The returned &lt;code&gt;Parsed&lt;/code&gt; points into that arena and into &lt;code&gt;argv&lt;/code&gt;, so both must outlive it. Inside &lt;code&gt;execute&lt;/code&gt; this is already the case.&lt;/li&gt;
&lt;li&gt;Flag values are slices of the original &lt;code&gt;argv&lt;/code&gt; strings, not copies.&lt;/li&gt;
&lt;li&gt;The command tree itself allocates nothing. It is &lt;code&gt;const&lt;/code&gt; data.
## Testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The module has 17 tests across four files. Run them with &lt;code&gt;zig build test&lt;/code&gt;, or &lt;code&gt;zig test src/cli/root.zig&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;parser.zig&lt;/code&gt;: subcommand and alias resolution, flag forms, clusters, negation, &lt;code&gt;--&lt;/code&gt;, env precedence, every error, required flags, arg count rules, help skipping validation.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;help.zig&lt;/code&gt;: exact text of root help, subcommand help, and the usage hint.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;execute.zig&lt;/code&gt;: for each outcome, checks stdout, stderr, and the exit code together.
To test your own commands, call &lt;code&gt;execute&lt;/code&gt; with in-memory writers:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight zig"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Allocating&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;allocator&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deinit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Allocating&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;allocator&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deinit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cli&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"x"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;allocator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;allocator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;stderr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expectEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;@as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expectEqualStrings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;written&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expectEqualStrings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;written&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Limits and known gaps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--version&lt;/code&gt;&lt;/strong&gt; only works as the only argument (&lt;code&gt;nusku --version&lt;/code&gt;). It is not a real flag, so &lt;code&gt;nusku query --version&lt;/code&gt; is an unknown flag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tokens like &lt;code&gt;-5&lt;/code&gt;&lt;/strong&gt; are treated as flags. To pass a negative number as a positional, put it after &lt;code&gt;--&lt;/code&gt;. As the value of a flag it works: &lt;code&gt;--limit -5&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shadowed flags.&lt;/strong&gt; If a command defines a local flag with the same name as an ancestor's persistent flag, lookup picks the local one, but both would share one key in &lt;code&gt;FlagValues&lt;/code&gt;. There is no check at tree-build time yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flag access is by string.&lt;/strong&gt; A misspelled name in &lt;code&gt;ctx.getInt("limt")&lt;/code&gt; panics at run time. The planned fix is typed flags derived from a struct with comptime reflection, so the compiler catches it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No color handling&lt;/strong&gt; yet, because nothing prints color. When something does, honor &lt;code&gt;NO_COLOR&lt;/code&gt; and check for a terminal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No shell completion&lt;/strong&gt; yet. The tree has what a generator needs (names, aliases, flags, kinds).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No "did you mean" suggestions&lt;/strong&gt; for mistyped commands.
Planned next: typed flags via comptime reflection, then shell completion for bash, zsh, and fish.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;main&lt;/code&gt; returns the exit code as a &lt;code&gt;u8&lt;/code&gt;. Zig's start code passes it to the OS.&lt;/p&gt;

&lt;p&gt;codebase: &lt;a href="https://github.com/nuskum/nusku" rel="noopener noreferrer"&gt;https://github.com/nuskum/nusku&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cli</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
      <category>zig</category>
    </item>
    <item>
      <title>Ep0: Starting Nusku, a continuous profiler for Linux, built in Zig, no shortcuts</title>
      <dc:creator>Ali Amer</dc:creator>
      <pubDate>Sun, 04 Oct 2026 20:54:59 +0000</pubDate>
      <link>https://dev.to/aliamerj/starting-nusku-a-continuous-profiler-for-linux-built-in-zig-no-shortcuts-13na</link>
      <guid>https://dev.to/aliamerj/starting-nusku-a-continuous-profiler-for-linux-built-in-zig-no-shortcuts-13na</guid>
      <description>&lt;p&gt;I've always wanted to see the complete performance picture before pushing any code or building something. Not after it's slow in production, not after a user complains about it, before. Click a button, run a command, trigger a request, and actually see what that cost instead of guessing.&lt;/p&gt;

&lt;p&gt;That itch has been sitting with me for years. Back in university, I took a course on application performance, and every single homework was the same loop: profile a feature, find what's slow, fix it, measure again, confirm it's actually better. We did it in C# with Visual Studio's live profiler, watching CPU and memory update in real time while clicking through an app. That loop never left me. A few years later I was using NetBird before it had anything close to a performance or observability story, and I kept thinking about the same gap from a completely different angle: infrastructure.&lt;/p&gt;

&lt;p&gt;So I'm building Nusku. A continuous profiling system for Linux, written in Zig, with eBPF doing the actual watching.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Point Nusku at a running process, or launch one through it, and watch what it's actually doing while you use it. CPU time per function, live. Memory as it's allocated, not just a number that goes up, but what called for it and where. Threads that look idle but are actually stuck waiting on a lock, a disk read, or another process, and for how long. Syscalls that quietly eat milliseconds a normal CPU graph never shows you. Network calls that are slow because packets are getting retransmitted, not because the code is doing anything wrong.&lt;/p&gt;

&lt;p&gt;It runs locally with nothing to configure, you point it at a process and watch. It can also run on a remote machine and stream that same live picture back over a secure connection, so a server can be watched the same way as something running on your own laptop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why eBPF, and why Zig
&lt;/h2&gt;

&lt;p&gt;Every serious Linux profiling tool, Parca, Pyroscope, Cilium, Datadog's continuous profiler, is built on eBPF, not on polling files in /proc and hoping for the best. eBPF is the only way to get real kernel-level visibility into what a process is doing, without injecting anything into it and without the overhead of a userspace polling loop. So that's the foundation, not an add-on bolted on later.&lt;/p&gt;

&lt;p&gt;Zig is the other half of the decision, and it's as much about the learning as the result. No garbage collector getting in the way of measuring performance accurately. Full control over memory and allocation, which matters a lot when the entire point of the tool is measuring memory and allocation in something else. And honestly, it's just the language I want to get good at.&lt;/p&gt;

&lt;h1&gt;
  
  
  The big picture
&lt;/h1&gt;

&lt;p&gt;Nusku splits into a few pieces, each with one job, built and released one at a time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agent: sits next to a process and does the actual watching through eBPF. Being built first, because nothing else matters if the data it collects isn't accurate.&lt;/li&gt;
&lt;li&gt;Controller: the thing you'd actually talk to once there's more than one process to watch, or watching needs to happen from somewhere else. Lists what's running, lets you pick a target, manages sessions.
...and others will decide later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each piece has to fully work before the next one starts. Right now, that means the Agent and nothing else.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I want to end up with
&lt;/h2&gt;

&lt;p&gt;Honestly, depth first. This is me practicing Zig and systems programming for real, no AI-generated slop, no shortcuts, just me, textbooks, and the Linux kernel docs. There's no deadline attached to any of this and no plan to monetize it right now. If it ends up being something other people actually find useful along the way, that's a genuine bonus, not the goal I'm optimizing for.&lt;/p&gt;

&lt;p&gt;What I do want is to end up with a tool that actually answers the question that's been sitting with me since that performance course: not "is my app slow," but "show me exactly why, right now, while it's happening."&lt;/p&gt;

&lt;p&gt;I'm documenting the real, unpolished progress in a dev log as I build it. If watching a profiler get built from scratch in Zig and eBPF, warts and all, sounds interesting, I'd genuinely like the company.&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>devtool</category>
      <category>architecture</category>
      <category>zig</category>
    </item>
    <item>
      <title>#DAY1: The story of unemployment peak for a software engineer</title>
      <dc:creator>Ali Amer</dc:creator>
      <pubDate>Sun, 31 May 2026 09:19:11 +0000</pubDate>
      <link>https://dev.to/aliamerj/day1-the-story-of-peak-unemployment-for-a-software-engineer-jf8</link>
      <guid>https://dev.to/aliamerj/day1-the-story-of-peak-unemployment-for-a-software-engineer-jf8</guid>
      <description>&lt;p&gt;𝗗𝗮𝘆 𝟭 𝗼𝗳 𝗨𝗻𝗲𝗺𝗽𝗹𝗼𝘆𝗺𝗲𝗻𝘁 𝗣𝗲𝗮𝗸&lt;/p&gt;

&lt;p&gt;I've been thinking what can I actually build that I 𝗿𝗲𝗮𝗹𝘆 𝗲𝗻𝗷𝗼𝘆 𝗺𝗮𝗸𝗶𝗻𝗴?&lt;/p&gt;

&lt;p&gt;I love Go. I love microservices. I really need a project to hone my skills and grow since I am unemployed and it is not easy to get a job that I would love and level up.&lt;/p&gt;

&lt;p&gt;I came up with a project – Wardu – Kubernetes native scheduler which allows running workers as containers in any language you like.&lt;/p&gt;

&lt;p&gt;𝗧𝗵𝗲 𝗶𝗱𝗲𝗮 𝗶𝘀 𝘀𝗶𝗺𝗽𝗹𝗲:  if you run microservices then you have workers implemented in different languages. Python for ML tasks, Go for core services, Node.js for notifications. But all existing schedulers require using their SDKs which makes you write Python/Go workers in second class.&lt;/p&gt;

&lt;p&gt;𝘀𝗼𝗹𝗲 𝗽𝗿𝗲𝗰𝗿𝗶­𝘁𝗶𝗼𝗻: 𝗮𝗰𝗰𝗲𝗽𝘁 𝗮 𝗝𝗦𝗢𝗡 𝗽𝗮𝘆𝗹𝗼𝗮𝗱, 𝗿𝗲𝘁𝘂𝗿𝗻 𝗿𝗲𝘀𝘂𝗹𝘁𝘀. 𝗛𝗧𝗧𝗣 𝗼𝗿 𝗴𝗥𝗣𝗖. 𝗔𝗻𝘆 𝗹𝗮𝗻𝗴𝘂𝗮𝗴𝗲. 𝗔𝗻𝘆 𝗰𝗼𝗻𝘁𝗮𝗶𝗻𝗲𝗿.&lt;/p&gt;

&lt;p&gt;Today I have prepared project structure, API Gateway, Docker configuration, Kubernetes manifests and started working on Tilt setup to test locally.&lt;/p&gt;

&lt;p&gt;Will this project be useful to anyone? Honestly I don't know yet.&lt;/p&gt;

&lt;p&gt;𝘉𝘶𝘵 𝘐'𝘮 𝘵𝘪𝘳𝘦𝘥 𝘰𝘧 𝘴𝘱𝘦𝘯𝘥𝘪𝘯𝘨 𝘩𝘰𝘶𝘳𝘴 𝘢𝘱𝘱𝘭𝘺𝘪𝘯𝘨 𝘵𝘰 𝘫𝘰𝘣𝘴 𝘢𝘯𝘥 𝘩𝘦𝘢𝘳𝘪𝘯𝘨 𝘯𝘰𝘵𝘩𝘪𝘯𝘨 𝘣𝘢𝘤𝘬. 𝘈𝘵 𝘭𝘦𝘢𝘴𝘵 𝘵𝘩𝘪𝘴 𝘸𝘢𝘺 𝘐'𝘮 𝘣𝘶𝘪𝘭𝘥𝘪𝘯𝘨 𝘴𝘰𝘮𝘦𝘵𝘩𝘪𝘯𝘨 𝘳𝘦𝘢𝘭.&lt;/p&gt;

</description>
      <category>career</category>
      <category>go</category>
      <category>kubernetes</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>The story of unemployment peak for a software engineer</title>
      <dc:creator>Ali Amer</dc:creator>
      <pubDate>Tue, 26 May 2026 11:07:48 +0000</pubDate>
      <link>https://dev.to/aliamerj/-1ain</link>
      <guid>https://dev.to/aliamerj/-1ain</guid>
      <description>&lt;p&gt;𝗜'𝘃𝗲 𝗯𝗲𝗲𝗻 𝘂𝗻𝗲𝗺𝗽𝗹𝗼𝘆𝗲𝗱 𝗳𝗼𝗿 𝗮𝗹𝗺𝗼𝘀𝘁 𝗮 𝘆𝗲𝗮𝗿.&lt;/p&gt;

&lt;p&gt;Back in 2020, I wrote down my dream job: work remotely on a well-known open source project. I built toward it : freelance contracts, internships, real experience. I felt like I was on track.&lt;/p&gt;

&lt;p&gt;Then the AI boom hit. Freelance dried up. Jobs disappeared.&lt;/p&gt;

&lt;p&gt;I did what every unemployed engineer does: built SaaS products. Three of them. All failed. Built a mobile app. Also failed.&lt;/p&gt;

&lt;p&gt;So I changed direction entirely. I made a list of Go open source projects and started contributing like I was employed by them. Got ghosted. PRs ignored. But eventually found one project where things clicked.&lt;/p&gt;

&lt;p&gt;My PRs got merged. The feedback was great. The CTO offered me a contract to implement bidirectional gRPC streaming for remote debugging and integrated with the UI&lt;/p&gt;

&lt;p&gt;Three months of hard work. It shipped to production.&lt;/p&gt;

&lt;p&gt;Then the contract ended. And I was unemployed again.&lt;/p&gt;

&lt;p&gt;That was almost a year ago.&lt;/p&gt;

&lt;p&gt;AI keeps getting better. Being "just" a full-stack Go engineer doesn't feel like enough anymore. And I can't level up the normal way through work because I don't have work.&lt;/p&gt;

&lt;p&gt;So here's my plan:&lt;/p&gt;

&lt;p&gt;I bought 7 DevOps and platform engineering courses. I'm going to study all of them and build one real, useful project from scratch  "𝗻𝗼 𝗔𝗜 𝘄𝗿𝗶𝘁𝗶𝗻𝗴 𝗺𝘆 𝗰𝗼𝗱𝗲, 𝗻𝗼 𝗰𝗹𝗼𝗻𝗲𝘀, 𝗻𝗼 𝘁𝗼𝘆 𝗮𝗽𝗽𝘀".&lt;/p&gt;

&lt;p&gt;I'll document everything. The learning. The building. The failures.&lt;/p&gt;

&lt;p&gt;𝘞𝘪𝘭𝘭 𝘪𝘵 𝘮𝘢𝘬𝘦 𝘮𝘦 𝘮𝘰𝘯𝘦𝘺? 𝗣𝗿𝗼𝗯𝗮𝗯𝗹𝘆 𝗻𝗼𝘁.&lt;br&gt;
𝘞𝘪𝘭𝘭 𝘪𝘵 𝘨𝘦𝘵 𝘮𝘦 𝘢 𝘫𝘰𝘣? 𝗠𝗮𝘆𝗯𝗲.&lt;/p&gt;

&lt;p&gt;I'm doing it anyway. Because I love this. And because I refuse to give up.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Stop maintaining two files that describe the same API</title>
      <dc:creator>Ali Amer</dc:creator>
      <pubDate>Sat, 18 Apr 2026 09:26:22 +0000</pubDate>
      <link>https://dev.to/aliamerj/stop-maintaining-two-files-that-describe-the-same-api-1ng8</link>
      <guid>https://dev.to/aliamerj/stop-maintaining-two-files-that-describe-the-same-api-1ng8</guid>
      <description>&lt;p&gt;Show of hands: how many of you have an OpenAPI spec and a separate test collection that slowly drifted apart?&lt;/p&gt;

&lt;p&gt;I'll wait.&lt;/p&gt;

&lt;p&gt;Yeah. I've been there too. For a long time. And I finally got annoyed enough to build something about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem in one sentence
&lt;/h2&gt;

&lt;p&gt;Your OpenAPI spec and your API tests describe the same thing, so they should be the same file.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;VolcAPI&lt;/strong&gt; — a Go CLI that makes your OpenAPI spec executable. You define test scenarios directly inside the spec using a &lt;code&gt;v-functional-test&lt;/code&gt; extension, then run them from the terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Inside your openapi.yml&lt;/span&gt;
&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/users&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create user&lt;/span&gt;
      &lt;span class="na"&gt;v-functional-test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;scenarios&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;create_valid_user"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;duplicate_email"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;scenarios&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;create_valid_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;
    &lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Alice&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alice@example.com&lt;/span&gt;
    &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;201&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="na"&gt;duplicate_email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Alice&lt;/span&gt;
      &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;existing@example.com&lt;/span&gt;
    &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;409&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;volcapi run volcapi_local.yml &lt;span class="nt"&gt;-o&lt;/span&gt; openapi.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done. Both your documentation and your tests updated in one place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment configs
&lt;/h2&gt;

&lt;p&gt;Point at different hosts without changing your spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# volcapi_local.yml&lt;/span&gt;
&lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://localhost:3000&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;API_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev_token_here&lt;/span&gt;

&lt;span class="c1"&gt;# volcapi_staging.yml  &lt;/span&gt;
&lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://staging.api.myapp.com&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;API_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${STAGING_TOKEN}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why Go?
&lt;/h2&gt;

&lt;p&gt;Single static binary. No node_modules. No Python virtualenv. Downloads in seconds, runs in CI with zero setup. Same reason K6 took off for performance testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it's at
&lt;/h2&gt;

&lt;p&gt;Early alpha. This is what works today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ OpenAPI 3.x parsing&lt;/li&gt;
&lt;li&gt;✅ GET / POST / PUT / DELETE&lt;/li&gt;
&lt;li&gt;✅ Response status + JSON body validation&lt;/li&gt;
&lt;li&gt;✅ Environment variable substitution&lt;/li&gt;
&lt;li&gt;🔨 JUnit XML output (for CI integration) — in progress&lt;/li&gt;
&lt;li&gt;🔨 GitHub Actions example — in progress
## The bigger picture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dredd used to own this space (OpenAPI-native API testing). It's been mostly unmaintained for years. Most of what replaced it is JavaScript-heavy or requires a cloud service. There's a real gap for a fast, Go-native, CI-first OpenAPI test runner. That's what I'm building.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/aliamerj/volcapi" rel="noopener noreferrer"&gt;https://github.com/aliamerj/volcapi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If this solves a problem you've felt — even a star helps me know the direction is right. And if you have opinions about the "spec as test suite" model (for or against), drop them in the comments. I'm still thinking through the design.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built in Go. MIT licensed. v0.1.0-alpha.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>go</category>
      <category>showdev</category>
      <category>testing</category>
    </item>
    <item>
      <title>Why does performance debugging still suck so much?</title>
      <dc:creator>Ali Amer</dc:creator>
      <pubDate>Wed, 08 Apr 2026 20:40:18 +0000</pubDate>
      <link>https://dev.to/aliamerj/why-does-performance-debugging-still-suck-so-much-17j2</link>
      <guid>https://dev.to/aliamerj/why-does-performance-debugging-still-suck-so-much-17j2</guid>
      <description>&lt;p&gt;Recently I got laid off.&lt;/p&gt;

&lt;p&gt;Weirdly, I didn’t feel stressed. I actually felt… excited.&lt;/p&gt;

&lt;p&gt;For a long time I’ve wanted to build something meaningful in open source, but work always got in the way. Suddenly I had the time to try.&lt;/p&gt;

&lt;p&gt;So I asked myself a question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does performance debugging on Linux still suck?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I remember a moment back in college that stuck with me.&lt;/p&gt;

&lt;p&gt;One semester we had to build a project using &lt;strong&gt;.NET on Windows&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
It was the first time I had to seriously use Windows for development.&lt;/p&gt;

&lt;p&gt;Honestly, I never liked .NET very much.&lt;/p&gt;

&lt;p&gt;But there was one thing I &lt;strong&gt;really liked&lt;/strong&gt;: Visual Studio.&lt;/p&gt;

&lt;p&gt;When you run an application in the debugger, Visual Studio shows you live metrics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU usage&lt;/li&gt;
&lt;li&gt;memory usage&lt;/li&gt;
&lt;li&gt;allocations&lt;/li&gt;
&lt;li&gt;performance spikes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the coolest part was watching what happened when you interacted with the app.&lt;/p&gt;

&lt;p&gt;You could click a button in the UI and instantly see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU usage spike&lt;/li&gt;
&lt;li&gt;memory allocations change&lt;/li&gt;
&lt;li&gt;performance graphs jump&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That made performance debugging &lt;strong&gt;feel visual and intuitive&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You could literally see:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I clicked this button… and something expensive just happened.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then you start digging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which function caused the spike?&lt;/li&gt;
&lt;li&gt;Why did memory jump here?&lt;/li&gt;
&lt;li&gt;What part of the code is slow?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It turned performance optimization into a kind of &lt;strong&gt;investigation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And I loved that experience.&lt;/p&gt;


&lt;h2&gt;
  
  
  But there was a problem
&lt;/h2&gt;

&lt;p&gt;Years later, working mostly on Linux systems, I realized something strange.&lt;/p&gt;

&lt;p&gt;We have powerful tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;perf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bpftrace&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;flamegraphs&lt;/li&gt;
&lt;li&gt;eBPF&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But none of them feel as &lt;strong&gt;simple and immediate&lt;/strong&gt; as that Visual Studio experience.&lt;/p&gt;

&lt;p&gt;Most tools are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hard to set up&lt;/li&gt;
&lt;li&gt;hard to understand&lt;/li&gt;
&lt;li&gt;designed for experts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I decided to build one.&lt;/p&gt;


&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;I’m building a tool called &lt;strong&gt;Nusku&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The goal is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A fast, modern performance profiler built with &lt;strong&gt;Rust + eBPF&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run instantly&lt;/li&gt;
&lt;li&gt;require almost no setup&lt;/li&gt;
&lt;li&gt;work on production systems&lt;/li&gt;
&lt;li&gt;show useful information immediately&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No complicated configuration.&lt;br&gt;&lt;br&gt;
No massive dashboards.&lt;/p&gt;

&lt;p&gt;Just run it and see where your CPU time is going.&lt;/p&gt;


&lt;h2&gt;
  
  
  What I’ve built so far
&lt;/h2&gt;

&lt;p&gt;So far Nusku can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attach to a running process&lt;/li&gt;
&lt;li&gt;Sample CPU usage using &lt;strong&gt;eBPF perf events&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Capture &lt;strong&gt;user-space stack traces&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Symbolize addresses into real function names&lt;/li&gt;
&lt;li&gt;Aggregate hot functions in real time&lt;/li&gt;
&lt;li&gt;Show CPU usage and memory usage in a live terminal view&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;── PID 132612 ── 98 samples ── CPU  99.0% ── RSS 2.0 MiB ── VIRT 3.1 MiB ──
     %     COUNT  FUNCTION                          SOURCE                           ADDRESS
────────────────────────────────────────────────────────────────────────────────────────────────
 27.6%        27  &amp;lt;core::ops::range::Range&amp;lt;T&amp;gt; as …  range.rs:773          0x00005efa8cfd4b95
 11.2%        11  testing::hot_c                    main.rs:7             0x00005efa8cfd4cc7
 10.2%        10  &amp;lt;i32 as core::iter::range::Step…  range.rs:197          0x00005efa8cfd4acd
 10.2%        10  &amp;lt;core::ops::range::Range&amp;lt;T&amp;gt; as …  range.rs:775          0x00005efa8cfd4b9f
  9.2%         9  testing::hot_c                    main.rs:8             0x00005efa8cfd4cf0
  6.1%         6  &amp;lt;core::ops::range::Range&amp;lt;T&amp;gt; as …  range.rs:771          0x00005efa8cfd4b69
  5.1%         5  &amp;lt;i32 as core::iter::range::Step…  range.rs:198          0x00005efa8cfd4b0b
  4.1%         4  core::hint::black_box             hint.rs:482           0x00005efa8cfd4d5a
  4.1%         4  core::iter::range::&amp;lt;impl core::…  range.rs:856          0x00005efa8cfd4b40
  3.1%         3  &amp;lt;core::ops::range::Range&amp;lt;T&amp;gt; as …  range.rs:776          0x00005efa8cfd4bba
  2.0%         2  &amp;lt;core::ops::range::Range&amp;lt;T&amp;gt; as …  range.rs:772          0x00005efa8cfd4b7a
  2.0%         2  &amp;lt;core::ops::range::Range&amp;lt;T&amp;gt; as …  range.rs:780          0x00005efa8cfd4bce
  2.0%         2  testing::hot_c                    main.rs:9             0x00005efa8cfd4d09
  2.0%         2  &amp;lt;i32 as core::iter::range::Step…  range.rs:195          0x00005efa8cfd4ac4
  1.0%         1  core::hint::black_box             hint.rs:483           0x00005efa8cfd4d64

Top frame:
  &amp;lt;core::ops::range::Range&amp;lt;T&amp;gt; as core::iter::range::RangeIteratorImpl&amp;gt;::spec_next
  /home/ali/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/range.rs
  line 773

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything runs in real time.&lt;/p&gt;

&lt;p&gt;Under the hood it uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rust&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;libbpf-rs&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;eBPF stack sampling&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blazesym&lt;/strong&gt; for symbolization&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What’s next
&lt;/h2&gt;

&lt;p&gt;Right now the output is pretty basic.&lt;/p&gt;

&lt;p&gt;Next steps include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;building a proper &lt;strong&gt;terminal UI (Ratatui)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;improving symbol aggregation&lt;/li&gt;
&lt;li&gt;better stack analysis&lt;/li&gt;
&lt;li&gt;eventually generating &lt;strong&gt;flamegraphs&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The long-term vision is a tool that feels as simple as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run with pid:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nusku &lt;span class="nt"&gt;--pid&lt;/span&gt; 1234
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;run binary:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nusku ./my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;run with commend:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nusku &lt;span class="nt"&gt;-c&lt;/span&gt; node app.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and immediately shows you where your program is spending CPU time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Follow the project
&lt;/h2&gt;

&lt;p&gt;I’ll be posting updates as I build this.&lt;/p&gt;

&lt;p&gt;If you're interested in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rust&lt;/li&gt;
&lt;li&gt;eBPF&lt;/li&gt;
&lt;li&gt;performance debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;follow along.&lt;/p&gt;

&lt;p&gt;GitHub repo:&lt;br&gt;
&lt;a href="https://github.com/aliamerj/nusku" rel="noopener noreferrer"&gt;https://github.com/aliamerj/nusku&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if you have ideas or feedback, I’d love to hear it.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>performance</category>
      <category>architecture</category>
      <category>devtool</category>
    </item>
    <item>
      <title>The Hidden Superpower of AI: How Smart Data Retrieval Is Revolutionizing Everyday Life</title>
      <dc:creator>Ali Amer</dc:creator>
      <pubDate>Mon, 19 May 2025 10:40:29 +0000</pubDate>
      <link>https://dev.to/aliamerj/the-hidden-superpower-of-ai-how-smart-data-retrieval-is-revolutionizing-everyday-life-3po2</link>
      <guid>https://dev.to/aliamerj/the-hidden-superpower-of-ai-how-smart-data-retrieval-is-revolutionizing-everyday-life-3po2</guid>
      <description>&lt;p&gt;A Day in the Life of “Invisible AI”&lt;br&gt;
Imagine waking up and asking your smart assistant for:&lt;/p&gt;

&lt;p&gt;“What’s my most urgent email? And remind me to follow up on that proposal if I haven’t heard back by Friday.”&lt;/p&gt;

&lt;p&gt;Your AI not only scans your inbox, it understands which messages matter most and taps into your calendar, too. No more frantic searches for a lost attachment or digging through threads—you get answers instantly.&lt;/p&gt;

&lt;p&gt;That’s the magic of AI‑powered retrieval, quietly hustling behind the scenes in apps you use every day.&lt;/p&gt;

&lt;p&gt;Why We Need Better Search—Again&lt;br&gt;
Keyword search felt revolutionary in the 2000s: type a phrase, get a list of links. But today, our data looks nothing like a neatly indexed webpage:&lt;/p&gt;

&lt;p&gt;Voice notes from last week’s brainstorming session&lt;br&gt;
PDFs with handwritten meeting minutes&lt;br&gt;
PowerPoint decks shared in chat groups&lt;br&gt;
Traditional search can’t connect the dots between “budget forecast” in a spreadsheet and that off‑hand mention in your voice memo. AI retrieval changes the game by understanding meaning, not just matching words.&lt;/p&gt;

&lt;p&gt;5 Surprising Ways AI Retrieval Helps You Right Now&lt;br&gt;
Smarter Personal Assistants&lt;/p&gt;

&lt;p&gt;Your AI can pull relevant documents, chats, and even images to answer complex questions:&lt;br&gt;
“Show me the contract version we signed last quarter and summarize its key dates.”&lt;br&gt;
Instant Knowledge Base for Teams&lt;/p&gt;

&lt;p&gt;No more “check our internal wiki”—AI bots can fetch and summarize policies, technical docs, or HR guidelines in natural language.&lt;br&gt;
Contextual Shopping Recommendations&lt;/p&gt;

&lt;p&gt;e‑commerce sites use retrieval to suggest products based on past purchases, reviews, and even your browsing behavior, all blended seamlessly.&lt;br&gt;
Legal &amp;amp; Medical Research Made Simple&lt;/p&gt;

&lt;p&gt;Lawyers and doctors can ask AI to surface past cases or similar clinical studies, saving hours of manual literature review.&lt;br&gt;
Home Automation with a Brain&lt;/p&gt;

&lt;p&gt;Ask your smart home:&lt;br&gt;
“Which lights were on last night between 10 PM and midnight?”&lt;br&gt;
and get a precise timeline without sifting through logs.&lt;br&gt;
The Flip Side: When Search Breaks&lt;br&gt;
Ever typed in a query and felt like your data was speaking a different language? That’s because:&lt;/p&gt;

&lt;p&gt;Synonyms and slang confuse keyword search.&lt;br&gt;
Unstructured formats (images, audio) aren’t easily indexed.&lt;br&gt;
Volume &amp;amp; velocity overwhelm simple databases.&lt;br&gt;
AI retrieval solves these by converting everything into “embeddings”—compact numerical fingerprints that capture meaning. Suddenly, “sales report Q1” and “Q1 numbers” live in the same semantic neighborhood.&lt;/p&gt;

&lt;p&gt;Your Next Step with AI Retrieval&lt;br&gt;
Curious to see how this works under the hood? Platforms like Dcup (open‑source RAG‑as‑a‑Service) let you:&lt;/p&gt;

&lt;p&gt;Connect all your data sources in minutes.&lt;br&gt;
Automatically chunk and embed documents, audio, and more.&lt;br&gt;
Deploy a smart search API that speaks natural language.&lt;br&gt;
Whether you’re building the next-gen digital assistant or just want your team to stop chasing files, AI‑powered retrieval is the secret sauce. Give it a spin—and transform chaos into clarity.&lt;/p&gt;

&lt;p&gt;Want to experience invisible AI retrieval for yourself?&lt;br&gt;
Head over to &lt;a href="https://dcup.dev" rel="noopener noreferrer"&gt;dcup.dev&lt;/a&gt; and start your free self‑hosted trial today! 🚀&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building Dcup: An Open-Source RAG Pipeline with a Twist</title>
      <dc:creator>Ali Amer</dc:creator>
      <pubDate>Sun, 04 May 2025 11:57:05 +0000</pubDate>
      <link>https://dev.to/aliamerj/building-dcup-an-open-source-rag-pipeline-with-a-twist-2ebn</link>
      <guid>https://dev.to/aliamerj/building-dcup-an-open-source-rag-pipeline-with-a-twist-2ebn</guid>
      <description>&lt;p&gt;Ever wrestled with a RAG pipeline and thought, &lt;strong&gt;“Why is this so damn hard?”&lt;/strong&gt; We’ve been there—tearing our hair out over complexity and slow performance. So, I built Dcup, an open-source, self-hostable RAG-as-a-Service platform that actually works Very good.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Dcup All About?
&lt;/h2&gt;

&lt;p&gt;It’s a tool to hook your app up to user data with zero bullshit. Here’s the rundown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tech Stack&lt;/strong&gt;: Next.js, BullMQ for job queues, OpenAI for embeddings, and Qdrant for vector storage. It’s lean, mean, and scales like a dream.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Killer Features&lt;/strong&gt;: AI-driven retrieval and hybrid search that cuts through the noise. Plus, optional re-ranking if you’re a precision freak.&lt;br&gt;
&lt;a href="https://dcup.dev/docs" rel="noopener noreferrer"&gt;Learn more&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At its core, Dcup is your bridge to smarter data handling. It takes the complexity out of RAG pipelines by automating the heavy lifting. Here’s how it works in plain English:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data Ingestion&lt;/strong&gt;: Hook up your data sources—think Google Drive, AWS, or wherever your files live—and Dcup keeps it all synced and current.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Processing&lt;/strong&gt;: Your raw data gets chopped into bite-sized chunks and turned into embeddings using OpenAI. (More on embeddings later—they’re cool!)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage &amp;amp; Indexing&lt;/strong&gt;: Those embeddings land in Qdrant, a lightning-fast vector database that makes searching a breeze.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced Retrieval&lt;/strong&gt;: Need answers fast? Features like re-ranking and hybrid search dig up the most relevant info in seconds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of Dcup as your personal AI assistant that organizes, understands, and retrieves your data like a pro.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fight We Won
&lt;/h2&gt;

&lt;p&gt;Big files used to choke us. Performance would tank, and we’d be staring at spinning wheels. Then we got smart—batching and parallel processing turned it around. Now, Dcup eats large datasets for breakfast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why You’ll Dig It
&lt;/h2&gt;

&lt;p&gt;This isn’t some corporate ad. It’s a project by dev not shitty vibe coding or whatever the kids call it these days.&lt;/p&gt;

&lt;h2&gt;
  
  
  support the project
&lt;/h2&gt;

&lt;p&gt;Star the repo if it vibes with you: &lt;a href="https://github.com/Dcup-dev/dcup" rel="noopener noreferrer"&gt;Dcup on GitHub&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wanna contribute? Hit me up! Let’s make RAG pipelines suck less together.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>opensource</category>
    </item>
    <item>
      <title>First Reverse Proxy Built with Zig</title>
      <dc:creator>Ali Amer</dc:creator>
      <pubDate>Mon, 25 Nov 2024 11:45:37 +0000</pubDate>
      <link>https://dev.to/aliamerj/first-reverse-proxy-built-with-zig-2hpi</link>
      <guid>https://dev.to/aliamerj/first-reverse-proxy-built-with-zig-2hpi</guid>
      <description>&lt;p&gt;The first-ever reverse proxy built with Zig. If you're a developer, sysadmin, or just someone interested in optimizing web traffic handling, Cloud Cup may be your new favorite tool. It's lightweight, fast, and built to scale. And it's open-source!&lt;/p&gt;

&lt;p&gt;In this blog, I'll walk you through Cloud Cup’s architecture and the challenges I faced while building it. I’ll also talk about how you can help shape the future of this project, suggest new features, and contribute. I am just getting started, and your feedback and support can make all the difference.&lt;br&gt;
What is Cloud Cup?&lt;/p&gt;

&lt;h1&gt;
  
  
  What is Cloud Cup?
&lt;/h1&gt;

&lt;p&gt;Cloud Cup is a reverse proxy and load balancer designed with simplicity and performance in mind. It helps route traffic between clients and backend servers, distributing the load evenly to avoid overloading any single server. But here’s the twist – Cloud Cup is built using Zig, a low-level language known for its performance and control over memory usage.&lt;/p&gt;

&lt;p&gt;Unlike traditional reverse proxies, which can be heavy on resources, Cloud Cup is lightweight, optimized for speed, and easy to configure. I believe it’s the next big thing in reverse proxies, and I want you to be a part of it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Architecture
&lt;/h1&gt;

&lt;p&gt;Cloud Cup’s architecture is designed to be as efficient and scalable as possible, and it revolves around the Master-Worker pattern. Let’s break it down:&lt;/p&gt;

&lt;h2&gt;
  
  
  Master Process
&lt;/h2&gt;

&lt;p&gt;The master process is the heart of Cloud Cup. Here's what it does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Configuration Management: The master process validates and applies configurations at startup, ensuring that everything is set up correctly before launching workers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dynamic Hot Reloading: Through the CLI tool (called cupctl), Cloud Cup can hot reload its configurations without needing to restart the entire system. This allows for seamless updates and scaling on the fly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Worker Management: The master spawns multiple worker processes and ensures that they are always running. If a worker crashes, the master immediately spawns a replacement.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CLI Listener: The master also manages the command-line interface, which lets you interact with Cloud Cup, run diagnostics, and apply updates.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Worker Process
&lt;/h2&gt;

&lt;p&gt;The worker process is responsible for handling actual network traffic:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Shared Port, Independent Threads: All workers listen on the same port, and the kernel manages the traffic distribution between them. Each worker has its own epoll instance for highly efficient I/O handling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Concurrency with Thread Pool: Each worker has a dedicated thread pool for handling requests concurrently, making Cloud Cup highly scalable and responsive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configuration Updates: The workers dynamically receive updates from the master process, so configuration changes are applied across all workers instantly and consistently.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Challenges I Faced
&lt;/h1&gt;

&lt;p&gt;Building Cloud Cup wasn’t without its challenges. Since I'm working with Zig, a language that is still relatively new in the ecosystem, I faced a steep learning curve when it came to optimizing networking performance and ensuring memory safety. But I was determined to keep the project lightweight, efficient, and easy to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some of the challenges I overcame include:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Memory Management: Zig’s manual memory management gave us more control, but also required careful handling to avoid memory leaks or crashes. I've worked hard to ensure that Cloud Cup’s memory footprint remains minimal while still delivering high performance.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Concurrency and Scalability: I wanted Cloud Cup to handle heavy loads without sacrificing performance. Building the master-worker architecture allowed us to handle traffic distribution efficiently while maintaining responsiveness.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But I'm not done yet! I'm actively working on adding more features and improving performance even further.&lt;/p&gt;

&lt;h1&gt;
  
  
  How Can You Contribute?
&lt;/h1&gt;

&lt;p&gt;As Cloud Cup is still in its early stages, I’d love your help! Whether you're a developer, tester, or just someone who wants to try out new software, you can contribute in many ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Feature Requests: Is there something you’d love to see in Cloud Cup? I'm listening! Submit your ideas, and I will review them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bug Reports: Encountered a bug? Let me know so I can fix it. Your feedback helps make Cloud Cup better for everyone.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Code Contributions: If you’re interested in contributing code, check out GitHub repo. I'd be happy to accept pull requests for new features or bug fixes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Suggestions: Have suggestions on how I can improve documentation or features? Drop me a message. I want to make Cloud Cup as user-friendly as possible.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find all the information you need for contributing in our CONTRIBUTING.md file.&lt;br&gt;
What’s Next?&lt;/p&gt;

&lt;p&gt;I'm just getting started with Cloud Cup, but I already have big plans for future updates. In the next releases, I'm planning to improve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance: Always looking to make Cloud Cup even faster!&lt;/li&gt;
&lt;li&gt;Features: More advanced load balancing options and enhanced configuration management.&lt;/li&gt;
&lt;li&gt;Documentation: I am committed to improving the docs, making it easier for developers to get started with Cloud Cup.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're passionate about reverse proxies, load balancing, or just want to see Cloud Cup grow, consider supporting me on our Patreon. Every bit of support helps me keep building and adding new features.&lt;/p&gt;

&lt;h2&gt;
  
  
  I Need Your Feedback!
&lt;/h2&gt;

&lt;p&gt;I want to make Cloud Cup the best reverse proxy tool out there, and for that, I need your feedback. Whether it’s a suggestion for a new feature, an improvement to the documentation, or a bug you’ve encountered, I want to hear from you.&lt;/p&gt;

&lt;p&gt;You can reach out via:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Issues: Create an Issue&lt;/li&gt;
&lt;li&gt;Email: Send me a message at &lt;a href="mailto:aliamer19ali@gmail.com"&gt;aliamer19ali@gmail.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am really excited about Cloud Cup, and I hope you’ll join me on this journey. Help me build the future of reverse proxies, one feature at a time!&lt;/p&gt;

&lt;p&gt;Thanks for reading, and I can’t wait to hear what you think!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Github&lt;/strong&gt;: &lt;a href="https://github.com/cloud-cup/cloud-cup" rel="noopener noreferrer"&gt;https://github.com/cloud-cup/cloud-cup&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;DOC&lt;/strong&gt;: &lt;a href="https://cloud-cup.netlify.app/" rel="noopener noreferrer"&gt;https://cloud-cup.netlify.app/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>zig</category>
      <category>proxy</category>
      <category>web</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
