<?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: puffball1567</title>
    <description>The latest articles on DEV Community by puffball1567 (@puffball1567).</description>
    <link>https://dev.to/puffball1567</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%2F4028036%2F318aece8-8f65-4e85-9c32-200838e42224.png</url>
      <title>DEV Community: puffball1567</title>
      <link>https://dev.to/puffball1567</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/puffball1567"/>
    <language>en</language>
    <item>
      <title>How to Write Object-Oriented Code in Go: Structs, Methods, Interfaces, and Composition</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Mon, 14 Sep 2026 02:59:00 +0000</pubDate>
      <link>https://dev.to/puffball1567/how-to-write-object-oriented-code-in-go-structs-methods-interfaces-and-composition-3fok</link>
      <guid>https://dev.to/puffball1567/how-to-write-object-oriented-code-in-go-structs-methods-interfaces-and-composition-3fok</guid>
      <description>&lt;p&gt;Go does not have classes or inheritance, but it does not prevent object-oriented design. It simply moves the building blocks into different language features: structs hold state, methods attach behavior, interfaces describe capabilities, and composition assembles collaborating parts.&lt;/p&gt;

&lt;p&gt;The productive question is not “how can I recreate a class hierarchy in Go?” It is “which data owns this behavior, and which dependency should this component require?” This article builds a small application service around that question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with data and behavior
&lt;/h2&gt;

&lt;p&gt;Suppose an application lets a user change their display name. The domain value is a struct, while the operation belongs on a service that has access to user storage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"errors"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;User&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;ID&lt;/span&gt;          &lt;span class="kt"&gt;int64&lt;/span&gt;
    &lt;span class="n"&gt;DisplayName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Repository&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;FindByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Service&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;repository&lt;/span&gt; &lt;span class="n"&gt;Repository&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt; &lt;span class="n"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Rename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&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;User&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"display name is required"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&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;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindByID&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="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;User&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="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DisplayName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
    &lt;span class="k"&gt;if&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;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Save&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="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;User&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;User&lt;/code&gt; is a value with data. &lt;code&gt;Service&lt;/code&gt; is a struct with a dependency and a cohesive operation. The constructor function makes the dependency explicit at creation time. None of these types needs a base class to have a clear responsibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Define interfaces where they are consumed
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Repository&lt;/code&gt; interface above belongs next to &lt;code&gt;Service&lt;/code&gt;, not necessarily next to a database implementation. The service states the smallest capability it needs: load a user and save a user. Any type with those methods satisfies the interface automatically.&lt;/p&gt;

&lt;p&gt;That has two useful consequences. Production code can use a PostgreSQL-backed repository, while a test can use a small in-memory fake. Neither implementation needs to import a shared “repository base class” or declare that it implements the interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;MemoryRepository&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;users&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MemoryRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;FindByID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;int64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&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;User&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user not found"&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;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;MemoryRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;user&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is dependency inversion without a framework. The application service depends on a capability, rather than on a particular storage driver.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prefer composition to inheritance
&lt;/h2&gt;

&lt;p&gt;Go supports struct embedding, but embedding is not a substitute for a deep inheritance tree. It promotes fields and methods from one struct into another; it does not give Go classes, virtual methods, or a general subtype model.&lt;/p&gt;

&lt;p&gt;For backend code, explicit fields are usually clearer than embedding. If a service needs a clock, a logger, and an email sender, name them as dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;EmailSender&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;NotificationService&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;users&lt;/span&gt; &lt;span class="n"&gt;UserLookup&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="n"&gt;EmailSender&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the object graph visible, keeps testing straightforward, and avoids coupling unrelated behavior through a shared parent type. Use embedding when the promoted behavior genuinely reads as part of the receiving type, not merely to reduce typing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep interfaces narrow and delay abstraction
&lt;/h2&gt;

&lt;p&gt;Not every struct needs an interface. A useful default is to create a concrete type first, then introduce an interface at the consumer boundary when a second implementation or a test seam actually exists.&lt;/p&gt;

&lt;p&gt;Large interfaces tend to become accidental framework contracts. Small interfaces are easier to implement, easier to test, and clearer about what an operation needs. Go’s standard library follows this style: a type only needs to provide the methods required by the consumer.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical Go OOP checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use structs to group state.&lt;/li&gt;
&lt;li&gt;Attach behavior with methods when that behavior naturally belongs to the state or service.&lt;/li&gt;
&lt;li&gt;Use constructor functions to make required dependencies explicit.&lt;/li&gt;
&lt;li&gt;Define small interfaces at the point where they are consumed.&lt;/li&gt;
&lt;li&gt;Prefer explicit composition to inheritance-shaped abstractions.&lt;/li&gt;
&lt;li&gt;Pass &lt;code&gt;context.Context&lt;/code&gt; through request-scoped backend operations.&lt;/li&gt;
&lt;li&gt;Keep transport concerns such as HTTP request parsing outside domain services.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach is object-oriented in the useful sense: state, behavior, dependencies, and boundaries have clear ownership. It just does not require a class hierarchy to achieve it. The official &lt;a href="https://go.dev/tour/methods/1" rel="noopener noreferrer"&gt;Go documentation on methods and interfaces&lt;/a&gt; is a good companion when learning the language mechanics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kinmokusei
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/puffball1567/kinmokusei" rel="noopener noreferrer"&gt;Kinmokusei&lt;/a&gt; is a programming language with TypeScript-inspired syntax that compiles to readable Go. It is intended for writing web backends and Go libraries while using the normal Go toolchain and package ecosystem directly.&lt;/p&gt;

</description>
      <category>go</category>
      <category>backend</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Kinmokusei v0.2.0: Generic OOP and Go Type Constraints for a TypeScript-Inspired Language</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Sat, 12 Sep 2026 18:37:19 +0000</pubDate>
      <link>https://dev.to/puffball1567/kinmokusei-v020-generic-oop-and-go-type-constraints-for-a-typescript-inspired-language-1joh</link>
      <guid>https://dev.to/puffball1567/kinmokusei-v020-generic-oop-and-go-type-constraints-for-a-typescript-inspired-language-1joh</guid>
      <description>&lt;p&gt;Kinmokusei v0.2.0 expands the language in the places where application code and the Go ecosystem meet: generic object-oriented programming, generic constraints, type aliases, and JSON interop. Kinmokusei uses TypeScript-inspired syntax, but compiles to readable Go and uses the ordinary Go toolchain and package ecosystem directly. This release makes several of those boundaries more capable without treating generated Go as an opaque implementation detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generic class inheritance and virtual dispatch
&lt;/h2&gt;

&lt;p&gt;Classes in Kinmokusei are reference types. v0.2.0 adds generic class static methods, generic class inheritance, and generic virtual dispatch. A generic derived class can inherit a base class with concrete or remapped type arguments, retain the inherited state, and override a virtual member while preserving the expected dispatch behavior through the hierarchy.&lt;/p&gt;

&lt;p&gt;That matters when a reusable abstraction carries its data type through more than one layer. A paged result, a domain-specific collection, or a typed application model should not need to discard its type parameter merely because it adds behavior in a derived class.&lt;/p&gt;

&lt;p&gt;The release also adds descendant-aware class downcasts. A checked downcast can recover a more specific type within the same class hierarchy while keeping the failure path explicit, rather than silently assuming that every base-class value has the desired derived shape.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;guide&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;animal&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;GuideDog&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="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;console&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;guide&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;speak&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;The checked form returns a value together with a boolean result. It makes the runtime boundary visible at the call site, while reference identity and virtual dispatch remain part of the class model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Go type-set constraints from Kinmokusei
&lt;/h2&gt;

&lt;p&gt;Go libraries increasingly express generic capabilities through interface type sets. v0.2.0 lets Kinmokusei use exported constraints from the Go standard library and external Go modules, rather than limiting generic code to &lt;code&gt;comparable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, code can use a constraint such as &lt;code&gt;cmp.Ordered&lt;/code&gt; when an operation really requires an ordered value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="nx"&gt;cmp&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cmp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;earlier&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Ordered&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;right&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;This is useful because it keeps the constraint owned by the Go package that defines it. Kinmokusei code can participate in the same generic API shapes as Go code, instead of recreating a parallel constraint vocabulary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generic aliases without raising the minimum Go version
&lt;/h2&gt;

&lt;p&gt;v0.2.0 also adds transparent generic aliases. An alias can give a reusable Kinmokusei name to a parameterized shape without introducing a new nominal type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;alias&lt;/span&gt; &lt;span class="nx"&gt;Values&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="nx"&gt;alias&lt;/span&gt; &lt;span class="nx"&gt;Lookup&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;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 compiler expands a generic alias to its instantiated underlying type at generated-Go boundaries. That detail is important for compatibility: the emitted code remains usable with the project's Go 1.23 minimum instead of depending on later Go generic-alias declarations. Kinmokusei tooling still retains the source-level alias, while Go consumers see the concrete Go type they expect.&lt;/p&gt;

&lt;p&gt;v0.2.0 also permits a native struct as the underlying representation of a distinct type. This gives application code a way to add nominal meaning to an existing value layout while keeping conversions explicit and retaining Go-compatible value behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stable JSON behavior for generic and inherited classes
&lt;/h2&gt;

&lt;p&gt;JSON is often the point where a type system meets an external API, stored data, or a frontend. v0.2.0 stabilizes JSON interop for generic classes: public fields use stable Kinmokusei field names, inherited public state is included, and private or protected state is excluded.&lt;/p&gt;

&lt;p&gt;Decoding into a class instance preserves the class's reference-oriented model. Constructor-created instances retain their identity and virtual dispatch behavior, rather than being reduced to an unstructured map at the application boundary. Where a wire format needs special initialization or a different representation, an application can still define its own JSON handling.&lt;/p&gt;

&lt;h2&gt;
  
  
  A release focused on typed boundaries
&lt;/h2&gt;

&lt;p&gt;These features are connected by the same goal: carry useful type information across the boundaries that real Go applications need to cross. Generic classes can model reusable reference-oriented behavior, Go type sets make interop constraints available directly, generic aliases keep source code readable without increasing the Go version requirement, and stable JSON rules make external data handling predictable.&lt;/p&gt;

&lt;p&gt;Kinmokusei v0.2.0 is available now. Developers with Go installed can try the release with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/puffball1567/kinmokusei/cmd/keika@v0.2.0
keika version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler supports Go 1.23 through Go 1.27. The release notes, source, and executable examples are available in the &lt;a href="https://github.com/puffball1567/kinmokusei" rel="noopener noreferrer"&gt;Kinmokusei repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>go</category>
      <category>typescript</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Introducing Kinmokusei: TypeScript-Inspired Syntax That Compiles to Readable Go</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Fri, 04 Sep 2026 16:31:55 +0000</pubDate>
      <link>https://dev.to/puffball1567/introducing-kinmokusei-typescript-inspired-syntax-that-compiles-to-readable-go-29ph</link>
      <guid>https://dev.to/puffball1567/introducing-kinmokusei-typescript-inspired-syntax-that-compiles-to-readable-go-29ph</guid>
      <description>&lt;p&gt;On August 31, 2026, I released &lt;a href="https://github.com/puffball1567/kinmokusei/releases/tag/v0.1.0" rel="noopener noreferrer"&gt;Kinmokusei v0.1.0&lt;/a&gt;, the first public preview of a programming language for writing web backends and Go libraries with TypeScript-inspired syntax and compiling them to readable Go source.&lt;/p&gt;

&lt;p&gt;The compiler command is named &lt;code&gt;keika&lt;/code&gt;, source files use the &lt;code&gt;.km&lt;/code&gt; extension, and the generated program remains part of the ordinary Go ecosystem: it uses Go modules, the Go toolchain, the Go ABI, the Go runtime, and existing Go packages directly. Go—not TypeScript—is the compatibility target.&lt;/p&gt;

&lt;p&gt;Repository: &lt;a href="https://github.com/puffball1567/kinmokusei" rel="noopener noreferrer"&gt;https://github.com/puffball1567/kinmokusei&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Kinmokusei is
&lt;/h2&gt;

&lt;p&gt;Kinmokusei is a source language for the Go ecosystem. It resembles TypeScript, but it is not TypeScript. It is intended for developers who like TypeScript-shaped syntax but want to build and publish ordinary Go programs and libraries. Its syntax is the approachable frontend; Go is the platform underneath and the main compatibility boundary.&lt;/p&gt;

&lt;p&gt;It is not a TypeScript-to-Go converter, transpiler, or TypeScript implementation. It does not accept &lt;code&gt;.ts&lt;/code&gt; files or existing TypeScript projects and turn them into Go. It is also not Go source with alternate punctuation. Kinmokusei is a separate language with its own &lt;code&gt;.km&lt;/code&gt; source, type system, and explicit constructs for behavior that must lower predictably to Go, including Go named types, pointers, multiple return values, interfaces, generics, channels, errors, and &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The central design choice is that generated Go is not a hidden temporary representation. It is deterministic, formatted with &lt;code&gt;gofmt&lt;/code&gt;, readable by a Go developer, and suitable for inspection or publication.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.km source
    ↓ keika check / build / emit-go
readable Go source
    ↓ standard Go toolchain
native executable or Go library
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Authors can distribute the &lt;code&gt;.km&lt;/code&gt; source, the generated Go module, or both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go compatibility instead of npm compatibility
&lt;/h2&gt;

&lt;p&gt;Kinmokusei deliberately does not target npm, Node.js, browser JavaScript, or the TypeScript package ecosystem. An npm package cannot be imported merely because the source looks similar to TypeScript, and JavaScript runtime behavior is not reproduced behind the generated program.&lt;/p&gt;

&lt;p&gt;That is an intentional tradeoff rather than a missing transpiler feature. By giving up npm compatibility, the language can concentrate on Go compatibility: existing Go module graphs, exported package data, named types, pointers, structs, interfaces and method sets, multiple results, generics, channels, &lt;code&gt;error&lt;/code&gt;, goroutines, the Go ABI, and the standard Go build toolchain.&lt;/p&gt;

&lt;p&gt;The practical ecosystem escape hatch is therefore &lt;code&gt;import go&lt;/code&gt;, not an npm compatibility layer. Higher-level Kinmokusei libraries can be built on that boundary, while the low-level types and behavior remain recognizable to Go developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  A first program
&lt;/h2&gt;

&lt;p&gt;The smallest program can import the real Go standard library through an explicit namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="nx"&gt;fmt&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fmt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&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="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello from Kinmokusei&lt;/span&gt;&lt;span class="dl"&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;p&gt;Save this as &lt;code&gt;hello.km&lt;/code&gt;, then check and run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;keika check hello.km
keika run hello.km
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;keika emit-go hello.km&lt;/code&gt; prints the generated source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Code generated by Kinmokusei. DO NOT EDIT.&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello from Kinmokusei"&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;There is no separate JavaScript runtime or compatibility layer in this path. The result is Go code that the normal Go toolchain can build.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript-inspired classes with Go underneath
&lt;/h2&gt;

&lt;p&gt;Kinmokusei provides reference-type classes, interfaces, visibility, constructors, and explicit single inheritance. These features lower to Go structs, methods, embedding, and interfaces instead of introducing a second object runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CounterValue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./counter-contract&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;CounterValue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;increment&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;current&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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;p&gt;Classes are reference types, while native &lt;code&gt;struct&lt;/code&gt; declarations provide nominal Go-style value types with copy semantics. The language also includes interfaces, generic functions and types, fixed arrays and slices, maps, enums, defined types, nullable references, explicit &lt;code&gt;Result&amp;lt;T&amp;gt;&lt;/code&gt; propagation, exceptions isolated from ordinary Go panics, channels, &lt;code&gt;select&lt;/code&gt;, goroutines, and structured &lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt; values.&lt;/p&gt;

&lt;p&gt;The aim is not to imitate every TypeScript or JavaScript behavior. The syntax should make the source approachable while preserving the distinctions that matter to generated Go, such as value versus reference semantics, copying versus aliasing, and a checked language &lt;code&gt;null&lt;/code&gt; versus raw Go &lt;code&gt;nil&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Go packages directly
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;import go&lt;/code&gt; form is the low-level bridge to the Go ecosystem. It works with both the standard library and external modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="nx"&gt;gin&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;github.com/gin-gonic/gin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;net/http&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;NewRouter&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;gin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Engine&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;New&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/health&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;gin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StatusOK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Kinmokusei&lt;/span&gt;&lt;span class="dl"&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;return&lt;/span&gt; &lt;span class="nx"&gt;router&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;The compiler reads exported Go package data and preserves Go types rather than flattening everything into a smaller foreign-function interface. Functions, constants, variables, structs, fields, methods, interfaces, pointers, multiple results, generics, callbacks, and explicit conversions are checked at the &lt;code&gt;.km&lt;/code&gt; source location.&lt;/p&gt;

&lt;p&gt;Kinmokusei can use an existing &lt;code&gt;go.mod&lt;/code&gt;, or it can manage a locked module graph through its project manifest and lockfile. Normal checking and building validate that graph without silently updating dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web backends are the first application target
&lt;/h2&gt;

&lt;p&gt;The initial release focuses on web backends, Go library production, and goroutine-based workloads. The repository contains a JSON API built directly on &lt;code&gt;net/http&lt;/code&gt;, an embedded HTTP package, and a full-stack example with one React and TypeScript frontend connected to interchangeable Gin and Fiber backends written in Kinmokusei.&lt;/p&gt;

&lt;p&gt;Both framework examples call the real upstream Go packages. They compile into ordinary Go services and are tested against independently handwritten Go implementations of the same HTTP contract.&lt;/p&gt;

&lt;p&gt;Kinmokusei also provides checked C boundaries for cases where Go is not the final consumer. Explicitly exported functions can produce a versioned C ABI, while incoming C FFI generation supports checked scalar, string, byte, array, struct, tagged-union, callback, error, handle, and ownership contracts.&lt;/p&gt;

&lt;h2&gt;
  
  
  The generated program must remain trustworthy
&lt;/h2&gt;

&lt;p&gt;A source-to-source language can look convincing while small semantic differences accumulate underneath. Kinmokusei therefore keeps a registry of Go-equivalent runtime contracts. In v0.1.0, all 75 registered contracts have isolated differential tests against independently handwritten Go programs.&lt;/p&gt;

&lt;p&gt;Generated programs are also compiled and tested across supported Go toolchains and target platforms. The release supports Go 1.23 through Go 1.27, and the published compiler is built with Go 1.27 so it can read export data produced by each supported toolchain.&lt;/p&gt;

&lt;p&gt;This does not mean that Kinmokusei already covers the entire Go language or ecosystem. It means that accepted behavior is expected to be explicit, testable, and predictable instead of being passed through silently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compiler and editor support ship together
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;keika&lt;/code&gt; distribution includes the compiler and &lt;code&gt;keika lsp --stdio&lt;/code&gt;. The official Visual Studio Code extension is a thin client for that same language server, so diagnostics and semantic behavior stay aligned with the compiler version.&lt;/p&gt;

&lt;p&gt;The current LSP provides diagnostics, hover, definition, references, rename, document symbols, completion, and signature help for Kinmokusei declarations and supported Go APIs. Matching VSIX files are attached to each release.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the public preview
&lt;/h2&gt;

&lt;p&gt;Go 1.23 through Go 1.27 are supported. If Go is already installed, the tagged compiler can be installed with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/puffball1567/kinmokusei/cmd/keika@v0.1.0
keika version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prebuilt archives for Linux, macOS, and Windows, checksums, and the matching Visual Studio Code extension are available on the &lt;a href="https://github.com/puffball1567/kinmokusei/releases/tag/v0.1.0" rel="noopener noreferrer"&gt;v0.1.0 release page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is a pre-1.0 public preview. Source syntax and generated APIs may change between minor releases, and several areas remain intentionally incomplete. The current goal is to make the implemented boundary solid enough for real experiments, feedback, and incremental expansion.&lt;/p&gt;

&lt;p&gt;If TypeScript-inspired source that produces readable Go sounds useful for a backend or library project, I would be glad to hear what you try, where the generated interface feels natural, and where it does not.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/kinmokusei" rel="noopener noreferrer"&gt;Kinmokusei repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/kinmokusei/releases/tag/v0.1.0" rel="noopener noreferrer"&gt;v0.1.0 release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/kinmokusei/blob/v0.1.0/docs/installation.md" rel="noopener noreferrer"&gt;Installation guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/kinmokusei/blob/v0.1.0/docs/language-design.md" rel="noopener noreferrer"&gt;Language design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/kinmokusei/tree/v0.1.0/examples/react-web-frameworks" rel="noopener noreferrer"&gt;React with Gin and Fiber example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>KoutenDB v0.14.0: A Self-Hosted Database with Docker and Verified Backups</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Sun, 30 Aug 2026 07:40:06 +0000</pubDate>
      <link>https://dev.to/puffball1567/koutendb-v0140-a-self-hosted-database-with-docker-and-verified-backups-37p8</link>
      <guid>https://dev.to/puffball1567/koutendb-v0140-a-self-hosted-database-with-docker-and-verified-backups-37p8</guid>
      <description>&lt;p&gt;Starting a database container is easy. Operating that database after the first successful request is the harder part.&lt;/p&gt;

&lt;p&gt;Backups need to be restorable, upgrades need a rollback path, certificates expire, unhealthy processes should not restart forever, and a database should not quietly cross an authorization boundary while answering a query.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/puffball1567/koutendb/releases/tag/v0.14.0" rel="noopener noreferrer"&gt;KoutenDB v0.14.0&lt;/a&gt;, released on August 27, 2026, focuses on that operational layer. It adds a reproducible self-host path around KoutenDB with versioned multi-architecture images, TLS and authentication defaults, verified recovery workflows, bounded supervision, approval-gated capacity plans, and additional confidentiality hardening.&lt;/p&gt;

&lt;p&gt;KoutenDB is a locality-first document and vector database written in Nim. Applications place related data into explicit coordinates called rings. At query time, KoutenDB first limits the candidates to the selected ring and then ranks the records within that smaller set. Previous releases established the storage, retrieval, persistence, cluster, and endurance foundations. Version 0.14.0 addresses the next practical challenge: operating the database on infrastructure controlled by its user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-hosting a database is a lifecycle, not a Docker command
&lt;/h2&gt;

&lt;p&gt;The new operational path is designed as a sequence of explicit, verifiable stages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Versioned KoutenDB image
          ↓
Non-root Docker Compose deployment
          ↓
TLS, authentication, health checks, strong durability
          ↓
Checkpoint → export → independent restore → verification
          ↓
Upgrade or certificate rotation with rollback
          ↓
Capacity observation → immutable plan → explicit approval
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Official OCI images are published for &lt;code&gt;linux/amd64&lt;/code&gt; and &lt;code&gt;linux/arm64&lt;/code&gt; through &lt;code&gt;ghcr.io/puffball1567/koutendb&lt;/code&gt;. The included single-node Compose bundle runs with a non-root user, a read-only container root, persistent strong-durability storage, generated TLS and authentication configuration, ring-local disk reads, and health checks.&lt;/p&gt;

&lt;p&gt;The bootstrap keeps secret values outside the JSON configuration files. It generates password and secret-key files, a local CA and server certificate for the initial deployment, and mounts the prepared runtime secrets read-only. The CA private key is not mounted into the database container.&lt;/p&gt;

&lt;p&gt;The intent is not to turn KoutenDB into a cloud provisioning platform. Docker, Kubernetes, Terraform, a virtualization platform, or a human operator can prepare CPU, memory, disks, and machines. KoutenDB is responsible for database-specific safety: validating its topology, producing recovery artifacts, checking whether prepared capacity is usable, and executing only typed database actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  A backup is not complete until a restored copy is verified
&lt;/h2&gt;

&lt;p&gt;Creating an archive is only the first half of a database backup. The important question is whether the exported generation can be restored and opened independently.&lt;/p&gt;

&lt;p&gt;The v0.14.0 self-host operator turns that into one controlled workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Drain writes
    ↓
Create an immutable checkpoint
    ↓
Verify the checkpoint
    ↓
Copy it under a staging name
    ↓
Restore it into an independent temporary volume
    ↓
Verify the restored data and segment layout
    ↓
Publish the backup generation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final backup directory appears only after the transported artifact and the independently restored copy pass verification. An interrupted or invalid candidate does not replace an existing destination.&lt;/p&gt;

&lt;p&gt;From the generated deployment directory, a manual checkpoint, export, and independent restore drill look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./operator.sh checkpoint-create before-upgrade

&lt;span class="nv"&gt;backup_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/mnt/koutendb-backups
./operator.sh checkpoint-export before-upgrade &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$backup_dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
./operator.sh restore-drill &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$backup_dir&lt;/span&gt;&lt;span class="s2"&gt;/before-upgrade"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same workflow is available as a scheduled backup operation. The included systemd timer runs the checkpoint, staged export, restore drill, verification, publication, and retention sequence under one lifecycle lock. Verified generations count toward retention; corrupt generations are preserved as diagnostic evidence instead of being treated as successful backups.&lt;/p&gt;

&lt;p&gt;The complete scheduled transaction can also be run manually. The final argument is the number of verified exports to retain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./operator.sh scheduled-backup /var/backups/koutendb 7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This distinction matters because a green “archive created” message does not prove that the database can recover from that archive. Version 0.14.0 makes the restore test part of the normal backup transaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rollback-safe database upgrades and certificate rotation
&lt;/h2&gt;

&lt;p&gt;The operator also handles upgrades using an explicitly versioned image or immutable digest. Mutable &lt;code&gt;latest&lt;/code&gt; images are rejected.&lt;/p&gt;

&lt;p&gt;Before replacement, the workflow validates the target image, drains and snapshots the active node, creates a checkpoint, and requires both the active and target images to verify that recovery generation. The configured image reference changes only after those checks. If the replacement fails its health check, the operator restores the previous image reference, recreates the service, verifies health, and resumes writes.&lt;/p&gt;

&lt;p&gt;An upgrade takes a pinned image reference and a checkpoint name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./operator.sh upgrade &lt;span class="se"&gt;\&lt;/span&gt;
  ghcr.io/puffball1567/koutendb:0.14.0 &lt;span class="se"&gt;\&lt;/span&gt;
  before-0.14.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Certificate rotation follows a similar staged model. Before changing the active certificate set, KoutenDB checks the CA, certificate chain, hostname, validity window, private key, and certificate/key match. A failed TLS health check restores the previous files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./operator.sh certificate-rotate &lt;span class="se"&gt;\&lt;/span&gt;
  /mnt/koutendb-pki/server.crt &lt;span class="se"&gt;\&lt;/span&gt;
  /mnt/koutendb-pki/server.key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Preflight new artifact
        ↓
Preserve a verified recovery point
        ↓
Stage and activate the change
        ↓
Health verification
   ↙ success      ↘ failure
keep new state     restore previous state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same basic rule is used across the lifecycle: do not discard the last known-good state before the replacement has proved that it can operate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capacity planning without arbitrary infrastructure hooks
&lt;/h2&gt;

&lt;p&gt;KoutenDB v0.14.0 records bounded numeric capacity history and can generate a provider-neutral plan from observed storage growth, memory, and CPU data.&lt;/p&gt;

&lt;p&gt;Each plan includes a forecast horizon, required headroom, topology intent, prerequisites, and the KoutenDB actions that would consume already prepared resources. Its ID is a SHA-256 digest of the complete versioned plan. Approval is bound to that exact content, and execution rechecks the plan identity, expiry, observation freshness, live growth, service health, and currently available resources.&lt;/p&gt;

&lt;p&gt;The operator does not execute an arbitrary shell hook or create cloud instances. Infrastructure can be prepared by the deployment's normal system; the approval gate then verifies that KoutenDB can safely use it. Modified, stale, repeated, unapproved, and under-provisioned plans are rejected.&lt;/p&gt;

&lt;p&gt;The workflow records observations first, creates a plan for a requested horizon, and then requires the exact generated plan ID for approval and execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./capacity.sh sample
./capacity.sh plan 604800

&lt;span class="nv"&gt;plan_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"PASTE_GENERATED_PLAN_ID_HERE"&lt;/span&gt;
./capacity.sh approve &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$plan_id&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
./capacity.sh execute &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$plan_id&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
./capacity.sh status &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$plan_id&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Stricter database access control and confidentiality boundaries
&lt;/h2&gt;

&lt;p&gt;The operational work is accompanied by a broader security hardening pass.&lt;/p&gt;

&lt;p&gt;Version 0.14.0 separates &lt;code&gt;reader&lt;/code&gt;, &lt;code&gt;writer&lt;/code&gt;, &lt;code&gt;replicator&lt;/code&gt;, and &lt;code&gt;admin&lt;/code&gt; roles. Node-to-node traffic uses explicit &lt;code&gt;peerAuth&lt;/code&gt; credentials instead of reusing an ordinary application identity. Authenticated sessions are bound to a galaxy, and ring authorization is applied to retrieval, listing, counting, querying, updates, deletes, and visible statistics.&lt;/p&gt;

&lt;p&gt;Other changes include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Argon2id password derivation and authenticated secretbox encryption for newly encrypted backups;&lt;/li&gt;
&lt;li&gt;owner-managed files or environment variables for backup passphrases;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0700&lt;/code&gt; POSIX data directories and &lt;code&gt;0600&lt;/code&gt; managed artifacts at creation time;&lt;/li&gt;
&lt;li&gt;rejection of symbolic-link output targets;&lt;/li&gt;
&lt;li&gt;request and response framing limits before allocation;&lt;/li&gt;
&lt;li&gt;bounded C ABI payload, vector, batch, string, boolean, and orbital inputs;&lt;/li&gt;
&lt;li&gt;stable remote error categories that do not expose internal exception text;&lt;/li&gt;
&lt;li&gt;fail-closed behavior for closed or unknown C ABI handles.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These boundaries are exercised by a checked-in &lt;a href="https://github.com/puffball1567/koutendb/blob/v0.14.0/docs/security-validation.md" rel="noopener noreferrer"&gt;Security Validation Matrix&lt;/a&gt;. The release validation covers authenticated TLS, galaxy and ring isolation, role and peer-service authorization, malformed protocol frames, encrypted-backup migration, POSIX artifact permissions, crash and storage-failure recovery, topology migration, coordinator failover, Universe synchronization, C ABI builds, and OCI image construction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying the v0.14.0 self-host bundle
&lt;/h2&gt;

&lt;p&gt;The complete self-host path is included in the release tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/puffball1567/koutendb.git
&lt;span class="nb"&gt;cd &lt;/span&gt;koutendb
git checkout v0.14.0

&lt;span class="nv"&gt;KOUTENDB_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.14.0 &lt;span class="se"&gt;\&lt;/span&gt;
  deploy/self-hosted/bootstrap.sh ../koutendb-deployment

&lt;span class="nb"&gt;cd&lt;/span&gt; ../koutendb-deployment
docker compose config
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
docker compose ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default listener binds to &lt;code&gt;127.0.0.1:7301&lt;/code&gt;. The generated bundle contains the Compose deployment, client and server configuration, operational scripts, recovery storage layout, and optional systemd timer units.&lt;/p&gt;

&lt;p&gt;The full commands for health verification, checkpoint export, restore drills, scheduled backups, upgrades, certificate rotation, and capacity plans are documented in the &lt;a href="https://github.com/puffball1567/koutendb/blob/v0.14.0/deploy/self-hosted/README.md" rel="noopener noreferrer"&gt;single-node self-host guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  From database features to a database that can be operated
&lt;/h2&gt;

&lt;p&gt;KoutenDB began with a retrieval model: place related data together and avoid making unrelated records candidates in the first place. That remains the database's central idea, but a useful database also needs a credible path from a local experiment to a service someone can recover, update, and supervise.&lt;/p&gt;

&lt;p&gt;Version 0.14.0 moves that path forward without hiding critical operations behind an unbounded automation layer. Recovery generations are verified, replacements keep a rollback route, capacity actions require exact approval, and authorization is applied at the data boundaries the database exposes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/koutendb/releases/tag/v0.14.0" rel="noopener noreferrer"&gt;KoutenDB v0.14.0 release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/koutendb/blob/v0.14.0/docs/v0.14-self-hosted-operations.md" rel="noopener noreferrer"&gt;v0.14 self-hosted operations design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/koutendb/blob/v0.14.0/docs/security-validation.md" rel="noopener noreferrer"&gt;Security Validation Matrix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/koutendb" rel="noopener noreferrer"&gt;KoutenDB repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>database</category>
      <category>docker</category>
      <category>opensource</category>
      <category>nim</category>
    </item>
    <item>
      <title>I Built a Kawaii Native GUI with Nim and SDL3</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Sat, 22 Aug 2026 11:34:31 +0000</pubDate>
      <link>https://dev.to/puffball1567/i-built-a-kawaii-native-gui-with-nim-and-sdl3-3bj2</link>
      <guid>https://dev.to/puffball1567/i-built-a-kawaii-native-gui-with-nim-and-sdl3-3bj2</guid>
      <description>&lt;p&gt;Native GUI demos often look like settings panels, administrative dashboards, or collections of standard controls. Those examples are useful, but they leave another question unanswered: can the same native UI foundation express a playful, consumer-facing visual language?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/puffball1567/clay-board-style-system/releases/tag/v0.5.0" rel="noopener noreferrer"&gt;Clay Board Style System v0.5.0&lt;/a&gt; includes a new Kawaii Companion demo built to explore that question. It renders a complete daily-companion concept in an SDL3 window with a soft color palette, mixed Japanese and English text, cards, progress indicators, and a two-head-tall character drawn directly through retained Canvas commands.&lt;/p&gt;

&lt;p&gt;There is no browser or WebView behind this screen, and the character is not a PNG, SVG, or 3D model. The demo is assembled from the same public Box, Text, Canvas, Style, layout, and retained-rendering primitives intended for ordinary CBSS applications.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Clay Board Style System is abbreviated as &lt;strong&gt;CBSS&lt;/strong&gt; throughout the rest of this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why visual range matters in native GUI development
&lt;/h2&gt;

&lt;p&gt;A UI toolkit can support rows, columns, text, borders, and input controls while still making every application look structurally similar. That is enough for many tools, but creative applications and consumer-facing desktop software also need control over composition, typography, color, illustration, spacing, and visual hierarchy.&lt;/p&gt;

&lt;p&gt;The Kawaii Companion demo is therefore not mainly a test of whether CBSS can draw rounded rectangles. It tests whether its existing primitives can be composed into a coherent visual system that feels different from a conventional native GUI demo.&lt;/p&gt;

&lt;p&gt;The screen combines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a two-column application layout built from retained Box nodes;&lt;/li&gt;
&lt;li&gt;Japanese and English text shaped through the configured text engine and font fallbacks;&lt;/li&gt;
&lt;li&gt;cards, borders, shadows, gradients, spacing, and rounded geometry expressed through Style values;&lt;/li&gt;
&lt;li&gt;a character illustration produced from Canvas drawing commands rather than an imported character asset;&lt;/li&gt;
&lt;li&gt;several visually distinct sections that still share one palette and typography system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is important for CBSS because the project aims to provide reusable presentation primitives, not a fixed collection of applications that all inherit one visual identity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a kawaii desktop UI without HTML or a WebView
&lt;/h2&gt;

&lt;p&gt;The authoring model is CSS-inspired, but CBSS is not a browser CSS implementation. The demo creates retained nodes and typed style values in Nim. CBSS then resolves style, computes layout, prepares text and paint data, and sends the result through its SDL3 backend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Nim application
  -&amp;gt; retained Box, Text, and Canvas nodes
  -&amp;gt; typed CBSS Style declarations
  -&amp;gt; style resolution, layout, text, and paint
  -&amp;gt; SDL3 backend
  -&amp;gt; native desktop window
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, the left companion panel is a normal Box with layout and visual declarations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;companion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uiStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;426&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;632&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;flexDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fdColumn&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"background-color"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colorValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;white&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="n"&gt;borderRadius&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;borderWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"border-color"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colorValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.38&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
  &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"box-shadow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shadowValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;px&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;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.24&lt;/span&gt;&lt;span class="p"&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;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Names such as &lt;code&gt;padding&lt;/code&gt;, &lt;code&gt;gap&lt;/code&gt;, &lt;code&gt;flexDirection&lt;/code&gt;, &lt;code&gt;background-color&lt;/code&gt;, and &lt;code&gt;box-shadow&lt;/code&gt; make the intent familiar to web developers. Internally, however, these declarations feed CBSS's retained native UI pipeline rather than a DOM and browser rendering engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drawing the character with retained Canvas commands
&lt;/h2&gt;

&lt;p&gt;The character is the most immediately visible part of the screenshot, but it does not require a separate illustration file. The demo constructs a &lt;code&gt;Canvas2D&lt;/code&gt;, records drawing commands, and mounts that canvas into the retained UI tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;mascot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newCanvas2D&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;mascot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drawMascot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;discard&lt;/span&gt; &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;mascot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;uiStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;380&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;378&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;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;companion&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"daily-companion-character"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;drawMascot&lt;/code&gt; layers rounded shapes, lines, a quadratic path, solid fills, and Oklab-interpolated linear gradients. The face, hair, clothing, highlights, shadow, and background decorations are all described in code. The result remains a 2D retained drawing; the sense of depth comes from layering, color, and highlights rather than a 3D asset pipeline.&lt;/p&gt;

&lt;p&gt;This does not mean every application should generate all artwork procedurally. It shows that custom visual elements can live beside styled layout and text without requiring a separate rendering model for each component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mixed Japanese and English typography in a native UI
&lt;/h2&gt;

&lt;p&gt;The screenshot also includes &lt;code&gt;きょうも、いい日。&lt;/code&gt; alongside English headings and supporting text. The demo configures the CBSS text path with font fallbacks before building the interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fonts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initFontRegistry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;fonts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addFallbackFamily&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Noto Sans"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;fonts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addFallbackFamily&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Noto Sans CJK JP"&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;cosmic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initCosmicTextEngine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fonts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;configureTextLayout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cosmic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textEngine&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;fonts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a small detail visually, but an important application-level test. A styled layout is not convincing if its text path works only for a narrow set of Latin demo strings. The Kawaii Companion screen exercises mixed-script shaping and fallback inside the same retained layout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the Kawaii Companion demo with Nim and SDL3
&lt;/h2&gt;

&lt;p&gt;The complete source is included in the v0.5.0 tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/puffball1567/clay-board-style-system.git
&lt;span class="nb"&gt;cd &lt;/span&gt;clay-board-style-system
git checkout v0.5.0
nimble setupBundled
nimble kawaiiCompanionDemo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current package requires Nim 2.2 or newer. Rust and Cargo are also needed to build the native text and image bridges. Linux x86_64 with SDL3 is the Tier 1 runtime target; the portable test suite also covers Windows x86_64 and macOS arm64, while their complete native runtime paths still need broader contributor validation.&lt;/p&gt;

&lt;p&gt;The full example is available in &lt;a href="https://github.com/puffball1567/clay-board-style-system/blob/v0.5.0/examples/kawaii_companion_demo.nim" rel="noopener noreferrer"&gt;&lt;code&gt;examples/kawaii_companion_demo.nim&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A native UI should not decide the personality of the application
&lt;/h2&gt;

&lt;p&gt;The main result of this demo is not that CBSS has a built-in “kawaii theme.” It does not. The result is that application code can construct this visual language from general-purpose native layout, text, Canvas, and style primitives.&lt;/p&gt;

&lt;p&gt;Native GUI development should not force every application toward the same visual personality. Kawaii Companion is one more test of whether CBSS can provide a familiar, expressive authoring model while remaining a native UI system rather than embedding a browser.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/clay-board-style-system/releases/tag/v0.5.0" rel="noopener noreferrer"&gt;Clay Board Style System v0.5.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/clay-board-style-system/blob/v0.5.0/examples/kawaii_companion_demo.nim" rel="noopener noreferrer"&gt;Kawaii Companion demo source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/clay-board-style-system" rel="noopener noreferrer"&gt;Project repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nim</category>
      <category>gui</category>
      <category>design</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Perry vs scriptc: Comparing Two TypeScript-to-Native Compilers</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Mon, 17 Aug 2026 16:19:37 +0000</pubDate>
      <link>https://dev.to/puffball1567/perry-vs-scriptc-comparing-two-typescript-to-native-compilers-464p</link>
      <guid>https://dev.to/puffball1567/perry-vs-scriptc-comparing-two-typescript-to-native-compilers-464p</guid>
      <description>&lt;p&gt;Two open-source projects now make a similar promise: write TypeScript or JavaScript, compile it ahead of time, and ship a native executable without requiring Node.js on the target machine.&lt;/p&gt;

&lt;p&gt;Those projects are &lt;a href="https://github.com/PerryTS/perry" rel="noopener noreferrer"&gt;Perry&lt;/a&gt; and &lt;a href="https://github.com/vercel-labs/scriptc" rel="noopener noreferrer"&gt;scriptc&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Their headlines sound similar, but they are not trying to build exactly the same product. Perry is growing into a cross-platform application platform with native UI, mobile targets, Node-compatible APIs, threading, and native integrations. scriptc is more narrowly centered on compiling ordinary TypeScript while making the boundary between static code, dynamic JavaScript, and unsupported code visible.&lt;/p&gt;

&lt;p&gt;That distinction matters more than a benchmark table.&lt;/p&gt;

&lt;p&gt;This comparison is based on each project's official repository, documentation, and published test methodology as reviewed on August 18, 2026. Perry was reviewed at commit &lt;a href="https://github.com/PerryTS/perry/tree/e53de8143fc89bcfde06cd139dd35350986e5c35" rel="noopener noreferrer"&gt;&lt;code&gt;e53de81&lt;/code&gt;&lt;/a&gt;, and scriptc at commit &lt;a href="https://github.com/vercel-labs/scriptc/tree/a737fde34653da0f7aaef72947c9ee146f093ca6" rel="noopener noreferrer"&gt;&lt;code&gt;a737fde&lt;/code&gt;&lt;/a&gt;. Both projects are moving quickly, so check their current documentation before making a production decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Perry vs scriptc: the short answer
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Perry&lt;/th&gt;
&lt;th&gt;scriptc&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Primary direction&lt;/td&gt;
&lt;td&gt;A broad native application platform built around TypeScript&lt;/td&gt;
&lt;td&gt;A TypeScript-to-native compiler with an explicit static/dynamic/rejected contract&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;SWC parser, then Perry's HIR and transforms&lt;/td&gt;
&lt;td&gt;The TypeScript compiler for parsing and type checking, then a typed IR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Native backend&lt;/td&gt;
&lt;td&gt;LLVM code generation and a system linker&lt;/td&gt;
&lt;td&gt;LLVM IR compiled by clang; a readable C backend is also available for inspection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default native path&lt;/td&gt;
&lt;td&gt;No JavaScript engine; an optional JS runtime requires explicit opt-in&lt;/td&gt;
&lt;td&gt;No JavaScript engine in a static build; &lt;code&gt;--dynamic&lt;/code&gt; explicitly embeds quickjs-ng&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TypeScript model&lt;/td&gt;
&lt;td&gt;A practical subset; most type annotations are erased&lt;/td&gt;
&lt;td&gt;Type information drives lowering, generic specialization, and checked boundaries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js compatibility&lt;/td&gt;
&lt;td&gt;A large native implementation of Node and web APIs, plus package-specific bindings&lt;/td&gt;
&lt;td&gt;Supported Node APIs are implemented in the native runtime and tested against Node behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;npm strategy&lt;/td&gt;
&lt;td&gt;Native bindings, selected packages compiled through &lt;code&gt;compilePackages&lt;/code&gt;, or an explicitly enabled JS runtime&lt;/td&gt;
&lt;td&gt;npm package code normally enters an explicit dynamic island; static npm compilation is experimental&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Native UI&lt;/td&gt;
&lt;td&gt;Built-in declarative UI mapped to AppKit, UIKit, GTK4, Win32, and the DOM&lt;/td&gt;
&lt;td&gt;No comparable application UI framework is documented&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-platform scope&lt;/td&gt;
&lt;td&gt;Desktop, Apple platforms, Android/Wear OS, and Web/WASM&lt;/td&gt;
&lt;td&gt;macOS, Linux, Windows, WASI, plus mobile library-mode archives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;Apache-2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In practical terms, Perry is the more application-oriented choice. scriptc is the more compiler-contract-oriented choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does "compile TypeScript to native" actually mean?
&lt;/h2&gt;

&lt;p&gt;Neither project simply runs &lt;code&gt;tsc&lt;/code&gt; and packages the resulting JavaScript. Both lower supported TypeScript or JavaScript constructs into native code and link support code for features such as strings, arrays, asynchronous work, and operating-system APIs.&lt;/p&gt;

&lt;p&gt;For Perry, the documented pipeline is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeScript
  -&amp;gt; SWC parser
  -&amp;gt; Perry HIR
  -&amp;gt; optimization and lowering passes
  -&amp;gt; LLVM
  -&amp;gt; object file
  -&amp;gt; system linker
  -&amp;gt; native executable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perry's &lt;a href="https://github.com/PerryTS/perry/blob/e53de8143fc89bcfde06cd139dd35350986e5c35/docs/src/contributing/architecture.md" rel="noopener noreferrer"&gt;architecture documentation&lt;/a&gt; also describes its runtime library, including JavaScript value representations, garbage collection, arrays, strings, and UI handles.&lt;/p&gt;

&lt;p&gt;For scriptc, the documented pipeline is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeScript
  -&amp;gt; TypeScript compiler: parse and type-check
  -&amp;gt; lowering
  -&amp;gt; typed IR
  -&amp;gt; LLVM IR
  -&amp;gt; clang
  -&amp;gt; native executable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;scriptc also retains a readable C backend for debugging and inspection. Its &lt;a href="https://github.com/vercel-labs/scriptc/blob/a737fde34653da0f7aaef72947c9ee146f093ca6/docs/src/app/how-it-works/page.mdx" rel="noopener noreferrer"&gt;How It Works documentation&lt;/a&gt; says that the LLVM backend is the production default and that runtime features are split into link-gated units.&lt;/p&gt;

&lt;p&gt;This is an important clarification: "no runtime" in compiler marketing should usually be read as "no Node.js or general JavaScript engine in the normal static output," not "the executable contains no support code at all." Both projects link native runtime components needed to preserve useful JavaScript behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  The biggest architectural difference: how TypeScript types are used
&lt;/h2&gt;

&lt;p&gt;Perry uses SWC to parse TypeScript and lowers the result into its own high-level intermediate representation. Its documentation describes support for a practical subset of TypeScript and states that interfaces, type aliases, and most annotations are erased. Perry also documents that annotations do not automatically become runtime validators. These rules are explicit in Perry's &lt;a href="https://github.com/PerryTS/perry/blob/e53de8143fc89bcfde06cd139dd35350986e5c35/docs/src/language/supported-features.md" rel="noopener noreferrer"&gt;supported features&lt;/a&gt; and &lt;a href="https://github.com/PerryTS/perry/blob/e53de8143fc89bcfde06cd139dd35350986e5c35/docs/src/language/limitations.md" rel="noopener noreferrer"&gt;limitations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;scriptc uses the TypeScript compiler itself for parsing and type checking. It then uses the checker's type and narrowing results when building its typed IR. Generics can be monomorphized, unions can become tagged representations, and crossings from dynamic code into statically typed code can be validated at runtime. The project documents this model in &lt;a href="https://github.com/vercel-labs/scriptc/blob/a737fde34653da0f7aaef72947c9ee146f093ca6/docs/src/app/how-it-works/page.mdx" rel="noopener noreferrer"&gt;How It Works&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That does not mean scriptc accepts every program that &lt;code&gt;tsc&lt;/code&gt; accepts. Its &lt;a href="https://github.com/vercel-labs/scriptc/blob/a737fde34653da0f7aaef72947c9ee146f093ca6/docs/src/app/limitations/page.mdx" rel="noopener noreferrer"&gt;limitations page&lt;/a&gt; says it uses its own type world based on &lt;code&gt;es2025&lt;/code&gt; plus its ambient declarations, and it deliberately tightens some signatures to match what the compiler can implement. A program may therefore need changes even if it already passes ordinary TypeScript checking.&lt;/p&gt;

&lt;p&gt;The trade-off is clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perry owns more of the language-to-runtime mapping and can shape it around a broad native platform.&lt;/li&gt;
&lt;li&gt;scriptc treats TypeScript's type information as part of the compilation contract and exposes a stricter boundary around what it can prove and lower.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Static compilation, dynamic JavaScript, and npm packages
&lt;/h2&gt;

&lt;p&gt;Both projects prefer engine-free native code, but neither can statically compile arbitrary JavaScript behavior. Dynamic property patterns, runtime-generated code, and real-world npm packages eventually force a choice: reject the code, port it, replace it with a native implementation, or embed an interpreter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Perry's npm and JavaScript runtime strategy
&lt;/h3&gt;

&lt;p&gt;Perry offers several paths for dependencies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;built-in or third-party native bindings can map familiar package APIs to Rust functions linked into the executable;&lt;/li&gt;
&lt;li&gt;selected pure TypeScript or JavaScript packages can be pulled into Perry's compiler through &lt;code&gt;perry.compilePackages&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;JavaScript files that need interpretation can use Perry's optional JS runtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important safety and deployment detail is that the interpreter is not supposed to appear silently. Perry's current &lt;a href="https://github.com/PerryTS/perry/blob/e53de8143fc89bcfde06cd139dd35350986e5c35/docs/src/cli/allow-js-runtime.md" rel="noopener noreferrer"&gt;JS runtime opt-in documentation&lt;/a&gt; describes &lt;code&gt;perry-jsruntime&lt;/code&gt; as QuickJS-based and says the build refuses to link it unless the application opts in through configuration, a CLI flag, or an environment variable.&lt;/p&gt;

&lt;p&gt;Perry also labels its current &lt;a href="https://github.com/PerryTS/perry/blob/e53de8143fc89bcfde06cd139dd35350986e5c35/docs/src/packages/porting.md" rel="noopener noreferrer"&gt;&lt;code&gt;compilePackages&lt;/code&gt; porting workflow as experimental&lt;/a&gt;. Packages based heavily on features such as &lt;code&gt;Proxy&lt;/code&gt;, or packages that depend on Node's native addon ABI, may need replacement or a Perry-specific native binding.&lt;/p&gt;

&lt;h3&gt;
  
  
  scriptc's dynamic island
&lt;/h3&gt;

&lt;p&gt;scriptc divides a program into three visible tiers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;code compiled statically;&lt;/li&gt;
&lt;li&gt;code that can run only when &lt;code&gt;--dynamic&lt;/code&gt; is enabled;&lt;/li&gt;
&lt;li&gt;code that is rejected.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Its &lt;a href="https://github.com/vercel-labs/scriptc/blob/a737fde34653da0f7aaef72947c9ee146f093ca6/docs/src/app/coverage/page.mdx" rel="noopener noreferrer"&gt;&lt;code&gt;coverage&lt;/code&gt; command&lt;/a&gt; reports how many statements compile statically and identifies dynamic or unsupported sites with diagnostic codes. A static build never silently adds an engine.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;--dynamic&lt;/code&gt; is enabled, scriptc embeds quickjs-ng for npm package implementations and &lt;code&gt;any&lt;/code&gt;-typed code. Values crossing back into static code are copied and validated. The &lt;a href="https://github.com/vercel-labs/scriptc/blob/a737fde34653da0f7aaef72947c9ee146f093ca6/docs/src/app/dependencies/page.mdx" rel="noopener noreferrer"&gt;npm dependencies documentation&lt;/a&gt; explicitly warns that CPU-bound dependency code in this island is slower than V8 and that its Node built-ins are shims rather than Node itself.&lt;/p&gt;

&lt;p&gt;scriptc also has an experimental &lt;code&gt;--npm-static&lt;/code&gt; mode, but its own documentation says package coverage is partial and unsupported sites can remain deferred or prevent static compilation.&lt;/p&gt;

&lt;p&gt;The key difference is not that one project has an escape hatch and the other does not. Both do. The difference is how prominently the boundary is exposed. scriptc makes the static/dynamic split a first-class report for each program, while Perry combines native compilation with package bindings, package porting, and explicit interpreter opt-in as part of a wider application platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Node.js compatibility: published claims are not the same as guarantees
&lt;/h2&gt;

&lt;p&gt;Perry documents native implementations for a broad set of Node modules and web APIs. Its current supported-features page reports roughly 97% passing results across 53 &lt;code&gt;node:*&lt;/code&gt; modules from Node's own test suite, with roughly 95% overall Node/TypeScript compatibility. Those are &lt;a href="https://github.com/PerryTS/perry/blob/e53de8143fc89bcfde06cd139dd35350986e5c35/docs/src/language/supported-features.md" rel="noopener noreferrer"&gt;Perry's published project results&lt;/a&gt;, not an independent certification that every Node application will compile unchanged.&lt;/p&gt;

&lt;p&gt;The limitations remain significant for some projects. Perry documents restrictions around runtime-generated code, general user-space CommonJS &lt;code&gt;require()&lt;/code&gt;, reflection, and dynamic language features. Its package-porting guide also explains why Node-API/N-API, NAN, V8, or libuv-based native addons cannot simply pass through &lt;code&gt;compilePackages&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;scriptc takes a different evidence approach. Its repository says the test corpus runs each program under Node and as a compiled binary, then compares stdout, stderr, and exit codes byte for byte. It also reruns the corpus with AddressSanitizer and a reference-count audit. That methodology is documented in both the &lt;a href="https://github.com/vercel-labs/scriptc/blob/a737fde34653da0f7aaef72947c9ee146f093ca6/README.md" rel="noopener noreferrer"&gt;repository README&lt;/a&gt; and &lt;a href="https://github.com/vercel-labs/scriptc/blob/a737fde34653da0f7aaef72947c9ee146f093ca6/docs/src/app/how-it-works/page.mdx" rel="noopener noreferrer"&gt;How It Works&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, scriptc does not claim universal Node compatibility. Its limitations documentation lists deliberate behavioral differences, unsupported standard-library surfaces, constraints on &lt;code&gt;any&lt;/code&gt;, dense-array behavior, FFI restrictions, and differences inside the dynamic island.&lt;/p&gt;

&lt;p&gt;So the fair reading is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perry publishes a wide compatibility surface and a high project-reported pass rate.&lt;/li&gt;
&lt;li&gt;scriptc emphasizes differential testing and enumerated divergences for the narrower surface it accepts.&lt;/li&gt;
&lt;li&gt;neither result means that an arbitrary Node.js application or npm dependency graph is guaranteed to work.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cross-platform native UI, mobile apps, and WebAssembly
&lt;/h2&gt;

&lt;p&gt;This is where Perry's broader product scope becomes most obvious.&lt;/p&gt;

&lt;p&gt;Perry includes a declarative &lt;a href="https://github.com/PerryTS/perry/blob/e53de8143fc89bcfde06cd139dd35350986e5c35/docs/src/ui/overview.md" rel="noopener noreferrer"&gt;&lt;code&gt;perry/ui&lt;/code&gt; framework&lt;/a&gt;. Its widgets map to AppKit on macOS, UIKit on iOS, GTK4 on Linux, Win32 on Windows, and DOM elements on the web. Perry's &lt;a href="https://github.com/PerryTS/perry/blob/e53de8143fc89bcfde06cd139dd35350986e5c35/docs/src/platforms/overview.md" rel="noopener noreferrer"&gt;platform overview&lt;/a&gt; also covers Android, Wear OS, watchOS, tvOS, visionOS, and WebAssembly-related targets.&lt;/p&gt;

&lt;p&gt;scriptc targets macOS, Linux, Windows, and WebAssembly through WASI Preview 1. Its current &lt;a href="https://github.com/vercel-labs/scriptc/blob/a737fde34653da0f7aaef72947c9ee146f093ca6/docs/src/app/platforms/page.mdx" rel="noopener noreferrer"&gt;platform documentation&lt;/a&gt; also describes iOS and Android static archives in library mode, intended to be linked into a host mobile application. It does not document a Perry-like cross-platform widget framework.&lt;/p&gt;

&lt;p&gt;That creates a straightforward decision boundary. If the goal is to build a native desktop or mobile interface directly from the same TypeScript-oriented platform, Perry is attempting to provide much more of the stack. If the goal is to compile typed application logic, a CLI, a server, a WASI module, or a library core while keeping the host UI separate, scriptc's narrower model may be easier to reason about.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about TypeScript-to-native performance?
&lt;/h2&gt;

&lt;p&gt;Perry publishes an open benchmark harness and reports both workloads it wins and workloads where Node.js or Bun is faster. That is better evidence than a table containing only favorable cases, and the exact harness is linked from Perry's &lt;a href="https://github.com/PerryTS/perry/blob/e53de8143fc89bcfde06cd139dd35350986e5c35/README.md#performance" rel="noopener noreferrer"&gt;README&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But those numbers do not answer "Is Perry faster than scriptc?" scriptc uses different runtime representations, a different compatibility contract, and different dynamic fallback behavior. No same-program, same-commit, same-machine comparison between the two projects was performed for this article.&lt;/p&gt;

&lt;p&gt;A fair benchmark would need to separate at least these cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fully static code in both compilers;&lt;/li&gt;
&lt;li&gt;code that activates either project's optional JavaScript engine;&lt;/li&gt;
&lt;li&gt;startup time, steady-state throughput, peak memory, and binary size;&lt;/li&gt;
&lt;li&gt;correct output and error behavior, not only elapsed time;&lt;/li&gt;
&lt;li&gt;the exact compiler commits, optimization flags, toolchain, and operating system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Until that experiment exists, a performance winner would be speculation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which TypeScript native compiler should you try?
&lt;/h2&gt;

&lt;p&gt;Try Perry first when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;native desktop or mobile UI is central to the project;&lt;/li&gt;
&lt;li&gt;you want a broad batteries-included platform around TypeScript;&lt;/li&gt;
&lt;li&gt;Perry's native modules or package-specific bindings cover your stack;&lt;/li&gt;
&lt;li&gt;its real-thread APIs and cross-platform application targets are important.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try scriptc first when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you want the TypeScript compiler's type information to drive native lowering;&lt;/li&gt;
&lt;li&gt;you need a report showing exactly which parts of your application stay static;&lt;/li&gt;
&lt;li&gt;explicit rejection is preferable to silently accepting unsupported behavior;&lt;/li&gt;
&lt;li&gt;you are building a CLI, server, WASI module, or embeddable native library without needing a built-in UI toolkit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For either project, start with a representative slice of the real application rather than a Fibonacci demo. Include the npm dependencies, Node APIs, error paths, asynchronous behavior, target platforms, and deployment constraints that will determine whether the experiment succeeds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Perry and scriptc share an attractive headline: compile TypeScript to native code and ship without requiring Node.js. Under that　headline, they optimize for different outcomes.&lt;/p&gt;

&lt;p&gt;Perry is building outward toward a complete cross-platform native application environment. scriptc is building inward around an inspectable compilation contract: static, explicitly dynamic, or rejected.&lt;/p&gt;

&lt;p&gt;That is why the useful question is not simply "Which compiler is faster?" It is "Which project's compatibility boundary and product scope match the application I want to build?"&lt;/p&gt;

&lt;h2&gt;
  
  
  Primary sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/PerryTS/perry/tree/e53de8143fc89bcfde06cd139dd35350986e5c35" rel="noopener noreferrer"&gt;Perry repository at the reviewed commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/PerryTS/perry/blob/e53de8143fc89bcfde06cd139dd35350986e5c35/docs/src/contributing/architecture.md" rel="noopener noreferrer"&gt;Perry compiler architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/PerryTS/perry/blob/e53de8143fc89bcfde06cd139dd35350986e5c35/docs/src/language/supported-features.md" rel="noopener noreferrer"&gt;Perry supported TypeScript features and Node.js compatibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/PerryTS/perry/blob/e53de8143fc89bcfde06cd139dd35350986e5c35/docs/src/language/limitations.md" rel="noopener noreferrer"&gt;Perry limitations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/PerryTS/perry/blob/e53de8143fc89bcfde06cd139dd35350986e5c35/docs/src/cli/allow-js-runtime.md" rel="noopener noreferrer"&gt;Perry JS runtime opt-in&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/PerryTS/perry/blob/e53de8143fc89bcfde06cd139dd35350986e5c35/docs/src/packages/porting.md" rel="noopener noreferrer"&gt;Perry package-porting guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/PerryTS/perry/blob/e53de8143fc89bcfde06cd139dd35350986e5c35/docs/src/ui/overview.md" rel="noopener noreferrer"&gt;Perry native UI overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/PerryTS/perry/blob/e53de8143fc89bcfde06cd139dd35350986e5c35/docs/src/platforms/overview.md" rel="noopener noreferrer"&gt;Perry platform overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vercel-labs/scriptc/tree/a737fde34653da0f7aaef72947c9ee146f093ca6" rel="noopener noreferrer"&gt;scriptc repository at the reviewed commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vercel-labs/scriptc/blob/a737fde34653da0f7aaef72947c9ee146f093ca6/docs/src/app/introduction/page.mdx" rel="noopener noreferrer"&gt;scriptc introduction and compilation tiers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vercel-labs/scriptc/blob/a737fde34653da0f7aaef72947c9ee146f093ca6/docs/src/app/how-it-works/page.mdx" rel="noopener noreferrer"&gt;scriptc architecture and differential testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vercel-labs/scriptc/blob/a737fde34653da0f7aaef72947c9ee146f093ca6/docs/src/app/coverage/page.mdx" rel="noopener noreferrer"&gt;scriptc coverage reports&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vercel-labs/scriptc/blob/a737fde34653da0f7aaef72947c9ee146f093ca6/docs/src/app/dependencies/page.mdx" rel="noopener noreferrer"&gt;scriptc npm dependency model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vercel-labs/scriptc/blob/a737fde34653da0f7aaef72947c9ee146f093ca6/docs/src/app/limitations/page.mdx" rel="noopener noreferrer"&gt;scriptc limitations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vercel-labs/scriptc/blob/a737fde34653da0f7aaef72947c9ee146f093ca6/docs/src/app/platforms/page.mdx" rel="noopener noreferrer"&gt;scriptc platform support&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Kinmokusei
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/puffball1567/kinmokusei" rel="noopener noreferrer"&gt;Kinmokusei&lt;/a&gt; is a programming language with TypeScript-inspired syntax that compiles to readable Go. It is intended for writing web backends and Go libraries while using the normal Go toolchain and package ecosystem directly.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>discuss</category>
      <category>aot</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Built CSS-Like Native UI Animations with Nim and SDL3</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Thu, 13 Aug 2026 02:03:48 +0000</pubDate>
      <link>https://dev.to/puffball1567/i-built-css-like-native-ui-animations-with-nim-and-sdl3-3jp8</link>
      <guid>https://dev.to/puffball1567/i-built-css-like-native-ui-animations-with-nim-and-sdl3-3jp8</guid>
      <description>&lt;p&gt;Can CSS-like keyframe animations and hover transitions work in a native GUI without a browser or WebView? &lt;a href="https://github.com/puffball1567/clay-board-style-system/releases/tag/v0.4.1" rel="noopener noreferrer"&gt;Clay Board Style System v0.4.1&lt;/a&gt;, released on August 12, 2026, includes a new SDL3 demo that shows them running in a native UI built with Nim.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/puffball1567/clay-board-style-system/releases/download/v0.4.1/ClayBoardStyleSystem_declarative_motion_demo.mp4" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foy4kcol3idlvc8yxz413.gif" alt="Clay Board Style System declarative motion demo" width="480" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the preview to open the full MP4 recording.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Clay Board Style System is abbreviated as &lt;strong&gt;CBSS&lt;/strong&gt; throughout the rest of this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why CSS-like animation is useful in native GUI development
&lt;/h2&gt;

&lt;p&gt;The first version of a native UI animation often starts with a timer and a mutable position:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;every frame
  -&amp;gt; calculate elapsed time
  -&amp;gt; interpolate a value
  -&amp;gt; update the widget
  -&amp;gt; rebuild what changed
  -&amp;gt; request another frame
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is manageable for one object. It becomes harder when one component combines a hover transition, several named animations, delays, easing functions, alternating directions, cancellation, and reduced-motion behavior.&lt;/p&gt;

&lt;p&gt;The host application also needs to distinguish work that changes layout from work that changes only presentation. Moving a box with a paint transform should not require the complete style and layout pipeline to run again on every frame.&lt;/p&gt;

&lt;p&gt;CBSS addresses this by keeping motion declarations in the style model and sampling only active tracks when their next frame deadline arrives.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Clay Board Style System v0.4.1 animation demo shows
&lt;/h2&gt;

&lt;p&gt;The declarative motion runtime itself landed in v0.4.0 on August 11. Version 0.4.1 is a smaller, intentionally focused release: it adds a polished example, GIF and MP4 recordings, and roadmap documentation for exposing declarative motion through the C ABI in Version 0.5 and later.&lt;/p&gt;

&lt;p&gt;The new demo makes four behaviors visible in one native SDL3 window:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;automatic horizontal travel with eased keyframes;&lt;/li&gt;
&lt;li&gt;an in-place 2D flip expressed with a typed transform;&lt;/li&gt;
&lt;li&gt;background color, text color, and opacity animations sharing one retained node;&lt;/li&gt;
&lt;li&gt;a reversible hover transition for color, opacity, translation, scale, and rotation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters because an API can look complete in unit tests while still being awkward to compose. The demo exercises multiple tracks, negative delays, alternate directions, inherited text color, hover state changes, paint, hit testing, and the SDL3 host loop together.&lt;/p&gt;

&lt;p&gt;Several objects moving smoothly at once is not unusual by itself; mature UI frameworks already do that. The purpose of this demo is to show that CSS-inspired keyframes and transitions can compose inside one retained native UI model, using typed Nim declarations and an SDL3 event loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing native keyframe animation without a manual interpolation loop
&lt;/h2&gt;

&lt;p&gt;The horizontal movement in the demo is registered as named style keyframes. The application describes the values at four offsets; the motion runtime owns timing and interpolation between them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;registerStyleKeyframes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;styleKeyframes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ease-shuttle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="n"&gt;styleKeyframe&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="o"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"transform"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transformValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;px&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;px&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="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;styleKeyframe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"transform"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transformValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;px&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;px&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="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;styleKeyframe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.88&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"transform"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transformValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;px&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="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;styleKeyframe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"transform"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transformValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;px&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="o"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The component then associates the animation name with its duration, timing function, iteration count, direction, and fill mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="n"&gt;uiStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="n"&gt;animationNames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ease-shuttle"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;animationDurations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.6'f32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;animationTimingFunctions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cubic-bezier(0.42, 0, 0.58, 1)"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"animation-iteration-count"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"infinite"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="n"&gt;animationDirection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;adAlternate&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;animationFillMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;afBoth&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 syntax is CSS-inspired, but the values are authored through typed Nim helpers. There is no DOM, WebView, JavaScript animation loop, or string selector involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running multiple CSS-like animations on one native UI node
&lt;/h2&gt;

&lt;p&gt;The color panel demonstrates a case that is easy to underestimate: two animations run on the same node with different durations and delays.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="n"&gt;uiStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="n"&gt;animationNames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"palette-cycle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"opacity-breathe"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;animationDurations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;6.2'f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.1'f32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;animationDelays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.0'f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.9'f32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;animationTimingFunctions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ease-in-out"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ease-in-out"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"animation-iteration-count"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"infinite, infinite"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="n"&gt;animationDirections&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;adAlternate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;adNormal&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;animationFillModes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;afBoth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;afBoth&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;CBSS keeps the tracks independent and applies declaration order when animated values overlap. CSS-like list cycling is supported for animation names, durations, delays, timing functions, iteration counts, directions, fill modes, play states, and composition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a reversible hover transition in a native GUI
&lt;/h2&gt;

&lt;p&gt;The interactive panel starts with normal style and adds a hover style. The transition declarations describe how the runtime moves between the two states.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="n"&gt;transitionProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"background-color"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"opacity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"transform"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;transitionDurations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.42'f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.24'f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.52'f32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;transitionTimingFunctions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;"ease-out"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"ease-out"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"cubic-bezier(0.16, 1, 0.3, 1)"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the pointer enters or leaves the button, CBSS resolves the new state and reconciles the transition once. Active samples then update paint data and, while geometry changes, hit-test data. Reversing the hover state starts from the current presented value rather than visibly jumping to an endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the SDL3 animation loop stays event-driven
&lt;/h2&gt;

&lt;p&gt;Declarative motion does not mean rendering continuously when nothing is active. The scheduler computes the next deadline and lets the SDL3 event loop wait until either an input/window event arrives or that deadline is due.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;state or input event
  -&amp;gt; resolve style and reconcile motion once
  -&amp;gt; schedule the next active-track deadline

deadline arrives
  -&amp;gt; sample active transitions and keyframes
  -&amp;gt; refresh paint and, if required, hit data
  -&amp;gt; render
  -&amp;gt; wait for the next event or deadline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important boundary is between layout and presentation. The demo's transforms, colors, and opacity can be sampled through paint and hit updates without running style resolution or layout on every animation frame. Layout still runs when a real layout-affecting change, such as a resize, makes it necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the Nim and SDL3 native UI animation demo
&lt;/h2&gt;

&lt;p&gt;The reproducible source for the recording is included in the v0.4.1 tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/puffball1567/clay-board-style-system.git
&lt;span class="nb"&gt;cd &lt;/span&gt;clay-board-style-system
git checkout v0.4.1
nimble setupBundled
nimble declarativeMotionDemo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current requirements are Nim 2.2 or newer plus Rust and Cargo for the native text and image bridges. Linux x86_64 with SDL3 remains the Tier 1 runtime target. Windows x86_64 and macOS arm64 are covered by the portable CI suite, but their complete native runtime paths still need contributor validation.&lt;/p&gt;

&lt;p&gt;The complete example is available in &lt;a href="https://github.com/puffball1567/clay-board-style-system/blob/v0.4.1/examples/declarative_motion_demo.nim" rel="noopener noreferrer"&gt;&lt;code&gt;examples/declarative_motion_demo.nim&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current limits of CSS-like native UI animation in CBSS
&lt;/h2&gt;

&lt;p&gt;Version 0.4.1 remains a developer preview, so public APIs may change before 1.0.&lt;/p&gt;

&lt;p&gt;The current declarative motion path focuses on opacity, foreground and background colors, and typed 2D transforms. Additional values, filters, 3D transforms, and CPU effects remain roadmap work. Reduced-motion handling exists in the Nim runtime, while foreign-language keyframe registration and transition control through the C ABI are planned for Version 0.5 and later; v0.4.1 documents that boundary but does not claim it is already implemented.&lt;/p&gt;

&lt;h2&gt;
  
  
  Release and demo timestamps
&lt;/h2&gt;

&lt;p&gt;GitHub lists v0.4.1 as a non-prerelease published at &lt;strong&gt;2026-08-12 11:26:51 JST&lt;/strong&gt; (&lt;code&gt;02:26:51 UTC&lt;/code&gt;). The GIF and MP4 demo assets were uploaded to the same release during the &lt;strong&gt;11:26 JST&lt;/strong&gt; minute. Both URLs in this article are pinned to v0.4.1 rather than the moving &lt;code&gt;main&lt;/code&gt; branch.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/clay-board-style-system/releases/tag/v0.4.1" rel="noopener noreferrer"&gt;Clay Board Style System v0.4.1 release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/clay-board-style-system/blob/v0.4.1/examples/declarative_motion_demo.nim" rel="noopener noreferrer"&gt;Declarative motion demo source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/clay-board-style-system" rel="noopener noreferrer"&gt;Project repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nim</category>
      <category>gui</category>
      <category>animation</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Implementing CSS ex and ch Units in a Native UI Style Engine</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Thu, 06 Aug 2026 12:57:16 +0000</pubDate>
      <link>https://dev.to/puffball1567/implementing-css-ex-and-ch-units-in-a-native-ui-style-engine-2433</link>
      <guid>https://dev.to/puffball1567/implementing-css-ex-and-ch-units-in-a-native-ui-style-engine-2433</guid>
      <description>&lt;p&gt;The &lt;code&gt;devel&lt;/code&gt; branch of Clay Board Style System now supports four more font-relative length units: &lt;code&gt;ex&lt;/code&gt;, &lt;code&gt;ch&lt;/code&gt;, &lt;code&gt;rex&lt;/code&gt;, and &lt;code&gt;rch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For readability, I will refer to Clay Board Style System as &lt;strong&gt;CBSS&lt;/strong&gt; below.&lt;br&gt;
CBSS is a CSS-inspired primitive engine for native GUI toolkits written mainly in Nim. It does not use a DOM or WebView, and it is not intended to reproduce the entire browser platform.&lt;/p&gt;

&lt;p&gt;This update began with a small-looking requirement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"min-width"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding four unit tags was easy. Resolving them correctly was not.&lt;/p&gt;

&lt;p&gt;Unlike &lt;code&gt;px&lt;/code&gt;, these units depend on the selected font. Unlike &lt;code&gt;em&lt;/code&gt;, they cannot be calculated from &lt;code&gt;font-size&lt;/code&gt; alone. The style resolver needs information from the text engine, but the core layout and style layers should not depend on one concrete font library.&lt;/p&gt;

&lt;p&gt;This devlog explains how commit&lt;br&gt;
&lt;a href="https://github.com/puffball1567/clay-board-style-system/commit/8629108cd263253a07c1d9680eb9f63dc6a2b2ae" rel="noopener noreferrer"&gt;&lt;code&gt;8629108&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
addresses that boundary.&lt;/p&gt;

&lt;p&gt;The feature is currently on &lt;code&gt;devel&lt;/code&gt; and is intended for the v0.4 development line. It is not part of the current v0.3.2 release.&lt;/p&gt;
&lt;h2&gt;
  
  
  What ex, ch, rex, and rch mean
&lt;/h2&gt;

&lt;p&gt;The distinction between the units matters to the implementation:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Unit&lt;/th&gt;
&lt;th&gt;Reference used by CBSS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ex&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;x-height of the current element's selected font&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;advance measure of the &lt;code&gt;0&lt;/code&gt; glyph in the current selected font&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rex&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;root element's &lt;code&gt;ex&lt;/code&gt; value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rch&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;root element's &lt;code&gt;ch&lt;/code&gt; value&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These definitions follow the font-relative units in the &lt;a href="https://www.w3.org/TR/css-values-4/#font-relative-lengths" rel="noopener noreferrer"&gt;CSS Values and Units specification&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The specification also defines a &lt;code&gt;0.5em&lt;/code&gt; fallback when the x-height or the horizontal &lt;code&gt;0&lt;/code&gt; advance cannot be determined.&lt;/p&gt;

&lt;p&gt;CBSS is CSS-inspired rather than a conforming browser engine, and the current adapter has one implementation difference worth making explicit. The CSS specification defines font-relative lengths without shaping, while the current cosmic-text adapter obtains &lt;code&gt;ch&lt;/code&gt; by shaping &lt;code&gt;"0"&lt;/code&gt; with the resolved text&lt;br&gt;
configuration. This article describes the CBSS contract as it exists in this commit, not a claim of complete CSS conformance.&lt;/p&gt;

&lt;p&gt;That fallback is important for a native style engine. CBSS must still behave deterministically in headless tests, through its C ABI, or when an application has not installed a full text engine.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why font-relative units cross subsystem boundaries
&lt;/h2&gt;

&lt;p&gt;A normal absolute length can be resolved inside the style system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;20px -&amp;gt; 20 layout pixels
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An &lt;code&gt;ex&lt;/code&gt; value needs a longer path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;font declarations
  -&amp;gt; selected font face and variation settings
  -&amp;gt; x-height in font units
  -&amp;gt; scale by the computed font size
  -&amp;gt; resolve ex into layout pixels
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ch&lt;/code&gt; also depends on the font selection, but its reference is the shaped advance of the &lt;code&gt;0&lt;/code&gt; glyph.&lt;/p&gt;

&lt;p&gt;CBSS already has a replaceable &lt;code&gt;TextEngine&lt;/code&gt; abstraction. Text shaping is provided by a cosmic-text adapter today, while layout and style resolution remain independent of cosmic-text. Importing the cosmic-text bridge directly into the property resolver would have broken that boundary.&lt;/p&gt;

&lt;p&gt;The new implementation therefore introduces a small callback contract instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;
  &lt;span class="n"&gt;FontUnitMetrics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt;
    &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;uint32&lt;/span&gt;
    &lt;span class="n"&gt;xHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;float32&lt;/span&gt;
    &lt;span class="n"&gt;zeroAdvance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;float32&lt;/span&gt;

  &lt;span class="n"&gt;FontUnitMetricsResolver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ComputedTextStyle&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;FontUnitMetrics&lt;/span&gt; &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;closure&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The style resolver describes the resolved font selection. The installed text adapter answers with only the two measurements required for these units. Neither side needs to own the other subsystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the metrics contract is versioned
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;FontUnitMetrics&lt;/code&gt; carries a contract version even though it currently contains&lt;br&gt;
only two values.&lt;/p&gt;

&lt;p&gt;That gives CBSS an explicit compatibility boundary. A future text adapter may be built separately from the style engine, especially when accessed through a native language boundary. Returning structurally similar data is not enough if the two sides disagree about its meaning.&lt;/p&gt;

&lt;p&gt;The resolver rejects an incompatible version and validates the fields independently. A non-finite or non-positive x-height does not invalidate a usable zero advance, and vice versa. Each invalid field falls back to &lt;code&gt;0.5em&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The same fallback is used when no provider is installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;halfEm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fontSize&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;

&lt;span class="n"&gt;FontUnitMetrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fontUnitMetricsContractVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;xHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;halfEm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;zeroAdvance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;halfEm&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps the default debug engine, headless style tests, and the C ABI deterministic without pretending that the fallback is a measured font metric.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resolution order is the difficult part
&lt;/h2&gt;

&lt;p&gt;The style resolver cannot ask for current font metrics before it knows the current font. At the same time, a declaration such as this refers to font metrics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"font-size"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The implementation resolves a node in stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Partition &lt;code&gt;font-size&lt;/code&gt;, &lt;code&gt;line-height&lt;/code&gt;, other &lt;code&gt;font-*&lt;/code&gt; descriptors, and the
remaining declarations.&lt;/li&gt;
&lt;li&gt;Resolve &lt;code&gt;font-size&lt;/code&gt; with the parent font metrics available.&lt;/li&gt;
&lt;li&gt;Resolve the descriptors needed to choose the current font.&lt;/li&gt;
&lt;li&gt;Ask the metrics provider for the current x-height and zero advance.&lt;/li&gt;
&lt;li&gt;Resolve &lt;code&gt;line-height&lt;/code&gt; and the remaining properties with the appropriate
current and root metrics.&lt;/li&gt;
&lt;li&gt;Pass stable root metrics and current metrics to child resolution.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This avoids a cycle and follows the important CSS distinction: font-relative units used in &lt;code&gt;font-*&lt;/code&gt; properties resolve from the parent metrics, while &lt;code&gt;ex&lt;/code&gt; and &lt;code&gt;ch&lt;/code&gt; used in &lt;code&gt;line-height&lt;/code&gt; continue to use the element's own metrics.&lt;/p&gt;

&lt;p&gt;Root variants need another invariant. &lt;code&gt;rex&lt;/code&gt; and &lt;code&gt;rch&lt;/code&gt; must continue to refer to the root text style while descendants and independently invalidated subtrees are resolved. CBSS stores the root x-height and root zero advance in the resolution environment rather than recalculating them from each descendant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring real metrics with cosmic-text
&lt;/h2&gt;

&lt;p&gt;The default full text adapter crosses from Nim into a Rust bridge built around cosmic-text.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;ex&lt;/code&gt;, the bridge selects the configured font face, applies variable-font settings, reads the face's x-height through &lt;code&gt;ttf-parser&lt;/code&gt;, and scales the result from font units to the computed font size. If the font does not provide a valid x-height, the bridge returns the half-em fallback.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;ch&lt;/code&gt;, it shapes the string &lt;code&gt;"0"&lt;/code&gt; with the resolved text configuration and uses the resulting layout-run width as the zero advance. If shaping cannot produce a positive finite value, it also falls back to half an em.&lt;/p&gt;

&lt;p&gt;The metrics are cached by the resolved font configuration. This matters because style resolution may ask the same question for many nodes. Reopening font data or shaping &lt;code&gt;0&lt;/code&gt; for every declaration would turn a small relative-unit feature into repeated text-engine work.&lt;/p&gt;

&lt;p&gt;The cache is deliberately bounded. In this commit, the text adapter retains at most 128 font-metric entries.&lt;/p&gt;

&lt;h2&gt;
  
  
  The public units remain typed
&lt;/h2&gt;

&lt;p&gt;The Nim API adds explicit constructors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;rex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;rch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Internally, the corresponding unit kinds were appended after the existing public ordinals. This preserves the numeric values of earlier unit tags.&lt;/p&gt;

&lt;p&gt;The versioned C ABI follows the same append-only rule. Its ABI version moves from &lt;code&gt;0x00010007&lt;/code&gt; to &lt;code&gt;0x00010008&lt;/code&gt;, and the four new unit constants are added without renumbering the existing ones.&lt;/p&gt;

&lt;p&gt;The C ABI uses the deterministic fallback metrics because a concrete text engine remains an application-side adapter concern at that boundary. A C consumer can therefore use &lt;code&gt;ex&lt;/code&gt; and &lt;code&gt;ch&lt;/code&gt;, but it should not mistake the fallback for a measurement from a particular installed font.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the tests protect
&lt;/h2&gt;

&lt;p&gt;The commit adds a dedicated 213-line unit test file and expands the cosmic-text and C-consumer tests.&lt;/p&gt;

&lt;p&gt;The test matrix covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stable public unit ordinals;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;0.5em&lt;/code&gt; behavior without a metrics provider;&lt;/li&gt;
&lt;li&gt;metrics based on the current font family and size;&lt;/li&gt;
&lt;li&gt;stable &lt;code&gt;rex&lt;/code&gt; and &lt;code&gt;rch&lt;/code&gt; values across descendants;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ex&lt;/code&gt; and &lt;code&gt;ch&lt;/code&gt; inside &lt;code&gt;line-height&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;independent fallback for invalid provider fields;&lt;/li&gt;
&lt;li&gt;diagnostics when standalone resolution has no font context;&lt;/li&gt;
&lt;li&gt;reuse of root and parent metrics during subtree resolution;&lt;/li&gt;
&lt;li&gt;cosmic-text x-height and zero-advance reporting;&lt;/li&gt;
&lt;li&gt;agreement between the reported zero advance and measured &lt;code&gt;0&lt;/code&gt; width;&lt;/li&gt;
&lt;li&gt;C ABI construction and layout using the fallback path.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is more test surface than the four new enum values suggest, but most bugs in relative-unit handling come from resolution context rather than arithmetic.&lt;/p&gt;

&lt;h2&gt;
  
  
  The larger design lesson
&lt;/h2&gt;

&lt;p&gt;CSS-inspired native UI is not mainly a matter of copying property names.&lt;br&gt;
Familiar syntax carries assumptions about font selection, inheritance, resolution order, fallback, and root-relative state.&lt;/p&gt;

&lt;p&gt;For CBSS, the useful design was not to make the style resolver understand cosmic-text. It was to define the smallest versioned question the resolver could ask a text engine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Given this resolved text style,
what are its x-height and zero-glyph advance?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That keeps the style system replaceable, the text engine replaceable, and the headless fallback deterministic.&lt;/p&gt;

&lt;p&gt;The result is a small author-facing feature built on an explicit subsystem boundary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uiStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"font-size"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"min-height"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&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;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Font-relative native layout"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repository:&lt;br&gt;
&lt;a href="https://github.com/puffball1567/clay-board-style-system" rel="noopener noreferrer"&gt;puffball1567/clay-board-style-system&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  About cosmic-text
&lt;/h2&gt;

&lt;p&gt;This implementation builds on &lt;a href="https://github.com/pop-os/cosmic-text" rel="noopener noreferrer"&gt;cosmic-text&lt;/a&gt;, a pure Rust library for multiline text shaping, layout, rendering, font fallback, and editing.&lt;/p&gt;

&lt;p&gt;cosmic-text was created by &lt;a href="https://github.com/jackpot51" rel="noopener noreferrer"&gt;Jeremy Soller&lt;/a&gt; and is developed in the &lt;a href="https://github.com/pop-os/cosmic-text" rel="noopener noreferrer"&gt;&lt;code&gt;pop-os/cosmic-text&lt;/code&gt;&lt;/a&gt; repository with contributions from its open-source community. CBSS uses it through a Rust bridge for native text shaping and measurement. The font-relative unit work in this devlog would be much larger without that foundation.&lt;/p&gt;

</description>
      <category>nim</category>
      <category>gui</category>
      <category>css</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Why Filtered Vector Search Returns Fewer Than K Results</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Thu, 06 Aug 2026 12:36:51 +0000</pubDate>
      <link>https://dev.to/puffball1567/why-filtered-vector-search-returns-fewer-than-k-results-23bo</link>
      <guid>https://dev.to/puffball1567/why-filtered-vector-search-returns-fewer-than-k-results-23bo</guid>
      <description>&lt;p&gt;Vector similarity is rarely the only rule that decides whether a result is useful.&lt;/p&gt;

&lt;p&gt;A support assistant may need documents for one tenant and one product version.&lt;br&gt;
A code assistant may need the active repository and branch. An internal RAG system may need to enforce permissions, publication status, language, and a date range before it returns any text to a model.&lt;/p&gt;

&lt;p&gt;These constraints are commonly called metadata filters. But adding a filter to an API request does not tell us when the filter is applied or how much vector search work it avoids.&lt;/p&gt;

&lt;p&gt;That distinction matters:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A filter can reduce the final result set without reducing the vector search.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article focuses on that execution boundary. A broader guide to reducing the candidate set is available in&lt;br&gt;
&lt;a href="https://dev.to/puffball1567/rag-retrieval-optimization-reduce-vector-search-before-ranking-5h16"&gt;RAG Retrieval Optimization: Reduce Vector Search Before Ranking&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Pre-filtering vs post-filtering in vector search
&lt;/h2&gt;

&lt;p&gt;Consider a search over one million document vectors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tenant_id = "acme"
product = "billing"
language = "en"
version = "2026.2"
published = true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suppose only 2,000 vectors satisfy all five conditions. The application asks for the ten most similar eligible documents.&lt;/p&gt;

&lt;p&gt;There are several ways a system can execute that request:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;th&gt;Simplified execution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Post-filtering&lt;/td&gt;
&lt;td&gt;Search broadly, then remove results that fail the filter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overfetch and post-filter&lt;/td&gt;
&lt;td&gt;Search for more than ten candidates, filter them, then keep ten&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pre-filtering&lt;/td&gt;
&lt;td&gt;Build an eligible set first, then search within it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filter-aware ANN search&lt;/td&gt;
&lt;td&gt;Use metadata indexes while traversing the vector index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Namespace or partition search&lt;/td&gt;
&lt;td&gt;Select a smaller stored subset, then run vector search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application routing&lt;/td&gt;
&lt;td&gt;Route directly to a known tenant, repository, or data neighborhood&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All six can expose an API that looks like "vector search with metadata filtering." Their cost and failure behavior are not the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why post-filtered vector search returns fewer than k results
&lt;/h2&gt;

&lt;p&gt;Post-filtering first asks the vector index for its nearest results and then removes candidates that do not satisfy the metadata conditions.&lt;/p&gt;

&lt;p&gt;If the unfiltered search returns 50 candidates but only 0.2 percent of the corpus is eligible, many requests may produce fewer than ten valid results.&lt;/p&gt;

&lt;p&gt;Some may produce none, even when matching documents exist elsewhere in the index.&lt;/p&gt;

&lt;p&gt;Overfetching reduces that risk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wanted results: 10
initial vector candidates: 500
apply metadata filter
return the first 10 eligible results
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the correct overfetch factor depends on filter selectivity and on how eligible vectors are distributed in the vector space. A fixed multiplier may work for one tenant and fail for another. A retry loop can improve completeness, but it adds work and makes tail latency harder to predict.&lt;/p&gt;

&lt;p&gt;Post-filtering is still reasonable when filters are broad, the candidate pool is already small, or occasional short result sets are acceptable. It is a poor default for strict authorization boundaries or highly selective filters.&lt;/p&gt;

&lt;p&gt;Weaviate's documentation describes the same two post-filtering risks: an unpredictable result count and the possibility that a restrictive filter leaves no match in the initial vector results. Its implementation uses pre-filtering with an allow list instead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://weaviate.io/developers/weaviate/concepts/filtering" rel="noopener noreferrer"&gt;Weaviate filtering concepts&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-filtering changes the eligible search space
&lt;/h2&gt;

&lt;p&gt;Pre-filtering determines which records are eligible before similarity ranking.&lt;br&gt;
A simplified plan looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;authorization filter
  -&amp;gt; tenant and product scope
  -&amp;gt; eligible vector IDs
  -&amp;gt; vector similarity search
  -&amp;gt; optional reranking
  -&amp;gt; context construction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the result contract easier to understand: the nearest neighbors are selected from the eligible set rather than selected globally and checked later.&lt;/p&gt;

&lt;p&gt;However, "pre-filtering" does not necessarily mean that the engine performs an exact scan over every eligible vector. An implementation may combine an inverted metadata index with an ANN graph, pass an allow list into graph traversal, choose between exact and approximate search based on cardinality, or use another filter-aware strategy.&lt;/p&gt;

&lt;p&gt;The important questions are therefore practical:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is eligibility established before results enter the top-k set?&lt;/li&gt;
&lt;li&gt;Can the vector index traverse efficiently under a selective filter?&lt;/li&gt;
&lt;li&gt;Does the engine fall back to an exact scan for some filter shapes?&lt;/li&gt;
&lt;li&gt;Which metadata fields require their own indexes?&lt;/li&gt;
&lt;li&gt;What happens when the eligible set is empty or extremely small?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, Qdrant recommends creating payload indexes for fields used in filters, preferably before ingestion. Its documentation treats fields such as availability, location, and price as business constraints that embeddings do not express reliably.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qdrant.tech/documentation/search/filtering/" rel="noopener noreferrer"&gt;Qdrant filtering documentation&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How filter selectivity changes vector search performance
&lt;/h2&gt;

&lt;p&gt;Filter selectivity is the fraction of the corpus that remains eligible. A filter matching 800,000 of one million records has 80 percent selectivity. A filter matching 2,000 records has 0.2 percent selectivity.&lt;/p&gt;

&lt;p&gt;That number can change the best execution plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a broad filter may add metadata work without removing many vector candidates;&lt;/li&gt;
&lt;li&gt;a selective post-filter may discard almost every ANN result;&lt;/li&gt;
&lt;li&gt;a selective pre-filter may make an exact scan of the eligible set practical;&lt;/li&gt;
&lt;li&gt;an intermediate filter may need filter-aware graph traversal;&lt;/li&gt;
&lt;li&gt;a stable and highly selective scope may be better represented by routing or
partitioning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not infer performance from the final result count. Two queries can both return ten rows while one considers thousands more vector candidates. Record eligible cardinality and vector work for every important filter shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  Metadata correctness comes before metadata performance
&lt;/h2&gt;

&lt;p&gt;Filtering is not only an optimization. Some filters define whether a document may be considered at all.&lt;/p&gt;

&lt;p&gt;A useful order for a RAG retrieval pipeline is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;enforce authorization and tenant isolation;&lt;/li&gt;
&lt;li&gt;select a stable application scope such as repository, product, or version;&lt;/li&gt;
&lt;li&gt;apply dynamic metadata such as status, language, and date;&lt;/li&gt;
&lt;li&gt;perform vector or hybrid candidate ranking;&lt;/li&gt;
&lt;li&gt;rerank the bounded candidate set;&lt;/li&gt;
&lt;li&gt;project fields and enforce the context budget.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Authorization should not depend on whether an ineligible vector happens to miss the top-k cutoff. It should be enforced as a hard boundary independently of relevance ranking.&lt;/p&gt;

&lt;p&gt;The metadata model also needs explicit semantics. For example:&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;Useful question&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tenant_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Is this a security boundary, a routing boundary, or both?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;repository&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Can one query intentionally search multiple repositories?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Should older versions be excluded or merely ranked lower?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;language&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Is fallback to another language allowed?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;published&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Can drafts ever enter model context?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;valid_from&lt;/code&gt; and &lt;code&gt;valid_to&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Which time is authoritative for the request?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A vector database cannot infer these policies from an embedding. The application still owns the meaning of the fields and the rules for combining them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tenant-scoped vector search: filter or namespace?
&lt;/h2&gt;

&lt;p&gt;Namespaces and partitions can reduce the search space before vector ranking, but they are not a replacement for metadata indexes.&lt;/p&gt;

&lt;p&gt;A good routing or partition boundary is usually:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;known before retrieval;&lt;/li&gt;
&lt;li&gt;stable enough that records do not move constantly;&lt;/li&gt;
&lt;li&gt;selective enough to exclude substantial unrelated data;&lt;/li&gt;
&lt;li&gt;meaningful to authorization or application behavior;&lt;/li&gt;
&lt;li&gt;limited to a manageable number of scopes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tenant, repository, product, and corpus source often fit. Free-form tags, temporary UI filters, arbitrary price ranges, and frequently changing status values usually fit metadata filtering better.&lt;/p&gt;

&lt;p&gt;For multi-tenant vector search, the choice depends on the workload. A namespace or routed partition makes tenant scope explicit and can avoid cross-tenant search work. A &lt;code&gt;tenant_id&lt;/code&gt; metadata filter is more flexible when queries may legitimately span tenants or when creating many physical scopes would be operationally expensive.&lt;/p&gt;

&lt;p&gt;Neither choice automatically implements authorization. Tenant identity must come from an authenticated context, and every retrieval path must enforce the same policy.&lt;/p&gt;

&lt;p&gt;This produces a layered design:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stable scope
  -&amp;gt; dynamic metadata filter
  -&amp;gt; vector or hybrid ranking
  -&amp;gt; reranking
  -&amp;gt; LLM context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The stable scope makes the first problem smaller. Metadata filters express conditions inside that scope. Vector similarity orders the remaining semantic candidates.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to measure vectors scanned in filtered vector search
&lt;/h2&gt;

&lt;p&gt;Returning ten results does not mean that only ten vectors were considered.&lt;br&gt;
When evaluating filtered vector search, record at least:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;total vectors in the corpus;&lt;/li&gt;
&lt;li&gt;vectors in the selected namespace or partition;&lt;/li&gt;
&lt;li&gt;records eligible after metadata filtering;&lt;/li&gt;
&lt;li&gt;vector candidates visited or scored;&lt;/li&gt;
&lt;li&gt;results remaining after filtering;&lt;/li&gt;
&lt;li&gt;recall at k for the eligible ground truth;&lt;/li&gt;
&lt;li&gt;empty-result and short-result rates;&lt;/li&gt;
&lt;li&gt;p50 and p95 retrieval latency;&lt;/li&gt;
&lt;li&gt;candidates passed to the reranker;&lt;/li&gt;
&lt;li&gt;tokens passed to the model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test several filter selectivities. A query matching 80 percent of a collection has a different execution shape from one matching 0.1 percent. Also test correlation: eligible vectors may be clustered together in the vector space or scattered across it.&lt;/p&gt;

&lt;p&gt;Finally, include a deliberately wrong scope in the evaluation. A narrow query can be fast because it excluded the correct documents. Latency improvement is not useful unless recall and authorization correctness remain intact.&lt;/p&gt;
&lt;h2&gt;
  
  
  A practical decision checklist
&lt;/h2&gt;

&lt;p&gt;Before shipping vector search metadata filtering, ask:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Which filters are authorization rules rather than relevance hints?&lt;/li&gt;
&lt;li&gt;Are filters applied before or after the vector top-k is formed?&lt;/li&gt;
&lt;li&gt;Which filter fields have indexes?&lt;/li&gt;
&lt;li&gt;How does performance change as filter selectivity changes?&lt;/li&gt;
&lt;li&gt;Can a stable namespace or application route reduce the search space first?&lt;/li&gt;
&lt;li&gt;Does overfetching hide an incomplete post-filtering design?&lt;/li&gt;
&lt;li&gt;Are recall, vectors examined, latency, and context tokens measured together?&lt;/li&gt;
&lt;li&gt;What is the fallback when the chosen scope is wrong or empty?&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Filtered vector search FAQ
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Does metadata filtering reduce vector search work?
&lt;/h3&gt;

&lt;p&gt;Only when the engine uses the filter before or during vector candidate selection, or when the application routes the query to a smaller scope. A post-filter can reduce the returned rows while leaving the original vector search unchanged.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why does vector search return fewer results than k?
&lt;/h3&gt;

&lt;p&gt;The corpus may contain fewer than &lt;code&gt;k&lt;/code&gt; eligible records. If enough eligible records do exist, a post-filter may have removed too many of the initial ANN candidates, or an approximate filtered search may have exhausted its search budget before finding &lt;code&gt;k&lt;/code&gt; matches.&lt;/p&gt;
&lt;h3&gt;
  
  
  Does pre-filtering reduce vector search recall?
&lt;/h3&gt;

&lt;p&gt;Recall must be measured against the eligible ground truth. A correct filter intentionally excludes out-of-scope records, but a wrong filter can exclude the answer. Filtered ANN traversal can also have different recall behavior from an unfiltered graph, so it should be compared with exact search over the same eligible set.&lt;/p&gt;
&lt;h3&gt;
  
  
  Should tenant ID use a metadata filter or a namespace?
&lt;/h3&gt;

&lt;p&gt;Use a namespace or routing boundary when tenant scope is stable, selective, and almost every query belongs to one tenant. Use an indexed metadata filter when the scope is dynamic or legitimate cross-tenant queries are common. Some systems combine both: route to a tenant-level scope, then apply metadata filters inside it.&lt;/p&gt;
&lt;h2&gt;
  
  
  A different approach: placement-aware retrieval in KoutenDB
&lt;/h2&gt;

&lt;p&gt;Metadata filtering is not the only way to reduce vector work before ranking.&lt;br&gt;
When a useful search scope is predictable while data is being stored, placement itself can provide the first candidate boundary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/puffball1567/koutendb" rel="noopener noreferrer"&gt;KoutenDB&lt;/a&gt; is an open-source document and vector database written in Nim.&lt;/p&gt;

&lt;p&gt;KoutenDB does not implement a ring as a conventional metadata filter. A ring is an application-defined locality coordinate chosen when data is placed. The same coordinate can later become the starting scope for retrieval.&lt;/p&gt;

&lt;p&gt;For example, documentation for different Laravel releases can remain in separate rings while a stellar lens records that they belong to the same framework context. The lens changes visibility metadata; it does not copy the document payloads between rings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;koutendb&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;koutendb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachStellar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"framework/laravel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"docs/laravel/10"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachStellar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"framework/laravel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"docs/laravel/11"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;queryVec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@[&lt;/span&gt;&lt;span class="mf"&gt;1.0'f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0'f32&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;relatedVersions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readStellar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"framework/laravel"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;queryVec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ring&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"docs/laravel/11"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;budget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The stellar read keeps the Laravel 10 and Laravel 11 results grouped by their original ring, so the application can see them as related without collapsing their version boundaries. &lt;code&gt;subrings&lt;/code&gt; can narrow a stellar read when only one member coordinate is needed.&lt;/p&gt;

&lt;p&gt;The current vector &lt;code&gt;retrieve&lt;/code&gt; API does not use a stellar lens as an implicit vector filter. After the appropriate member ring is selected, KoutenDB scans the vectors stored in that ring, computes their exact cosine similarity, and returns up to eight results. It does not first run a global vector search and discard results from other versions afterward.&lt;/p&gt;

&lt;p&gt;This is placement-aware retrieval rather than general-purpose metadata filtering. Rings preserve stable distinctions such as framework version, tenant, product, language, or document family. Stellar lenses can expose related rings through one read context without erasing those distinctions.&lt;br&gt;
Frequently changing conditions such as publication status or price may still need ring-read filters, application logic, or an indexed metadata filtering system. A ring or lens also should not be treated as authorization by itself;&lt;/p&gt;

&lt;p&gt;authentication and access policy remain separate concerns.&lt;/p&gt;

&lt;p&gt;The trade-off is explicit. KoutenDB works best when the application, import rule, or operator can express a useful locality while storing the data. If no meaningful locality is known, retrieval without a ring becomes a broad exact scan. KoutenDB also exposes retrieval statistics such as &lt;code&gt;totalVectors&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;scanned&lt;/code&gt;, &lt;code&gt;skippedVectors&lt;/code&gt;, and &lt;code&gt;candidateReduction&lt;/code&gt;, making the amount of vector work observable instead of inferring it from the number of returned results.&lt;/p&gt;

&lt;p&gt;The goal is not to avoid vector search. It is to make vector search operate on the smallest valid candidate set. Metadata filters determine which records are\ eligible. When a useful scope is known in advance, placement-aware retrieval can make that eligible set smaller before ranking starts.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>rag</category>
      <category>performance</category>
    </item>
    <item>
      <title>KoutenDB v0.10.0: 72 Hours, Three Persistent Nodes, and Zero Client Errors</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Mon, 03 Aug 2026 12:23:51 +0000</pubDate>
      <link>https://dev.to/puffball1567/koutendb-v0100-72-hours-three-persistent-nodes-and-zero-client-errors-1n2m</link>
      <guid>https://dev.to/puffball1567/koutendb-v0100-72-hours-three-persistent-nodes-and-zero-client-errors-1n2m</guid>
      <description>&lt;p&gt;I released &lt;strong&gt;KoutenDB v0.10.0&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Release:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/puffball1567/koutendb/releases/tag/v0.10.0" rel="noopener noreferrer"&gt;https://github.com/puffball1567/koutendb/releases/tag/v0.10.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;KoutenDB is a ring-oriented document and vector database written in Nim. Its core idea is to make an application's locality boundary part of the read path:&lt;br&gt;
place related data together, select that boundary before retrieval, and avoid making unrelated records candidates in the first place.&lt;/p&gt;

&lt;p&gt;That idea needs more than a clean benchmark. A database also has to keep running while it writes persistent state, serves reads, tracks topology state, and eventually shuts down for verification.&lt;/p&gt;

&lt;p&gt;For v0.10.0, I ran a local persistent three-node cluster continuously for 72 hours. The result was simple and useful: the run completed with zero client errors, and all three data directories passed offline verification after the servers stopped.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the endurance run did
&lt;/h2&gt;

&lt;p&gt;The runner started three local TCP nodes with persistence enabled and exercised&lt;br&gt;
a mixed workload every 250 ms for 259,200 seconds.&lt;/p&gt;

&lt;p&gt;The workload included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TCP writes;&lt;/li&gt;
&lt;li&gt;point reads using IDs returned by earlier writes;&lt;/li&gt;
&lt;li&gt;JSON projection queries;&lt;/li&gt;
&lt;li&gt;bounded ring reads with sort and limit;&lt;/li&gt;
&lt;li&gt;ring-scoped exact vector retrieval;&lt;/li&gt;
&lt;li&gt;metrics collection;&lt;/li&gt;
&lt;li&gt;a final snapshot and offline verification after shutdown.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The runner builds its own copy of the server, CLI, and workload binaries under the run directory before it starts. This matters because rebuilding the source tree during a long run cannot silently change the executable being tested.&lt;/p&gt;

&lt;p&gt;The exact run used commit &lt;code&gt;87c755c9130ec0bbf70a3903c75fd2bdae8b084b&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Final result
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Completed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;td&gt;969,281&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;returned-ID GET&lt;/td&gt;
&lt;td&gt;969,281&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;projection query&lt;/td&gt;
&lt;td&gt;969,281&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bounded ring read&lt;/td&gt;
&lt;td&gt;969,281&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ring-scoped retrieve&lt;/td&gt;
&lt;td&gt;96,928&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;metrics read&lt;/td&gt;
&lt;td&gt;48,464&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;client errors&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That is &lt;strong&gt;4,022,516 completed logical client operations&lt;/strong&gt; across the 72-hour period.&lt;/p&gt;

&lt;p&gt;After the workload finished, the runner took its final snapshot, stopped the three nodes, and ran offline &lt;code&gt;kouten verify&lt;/code&gt; against every persistent data directory. All three verifications succeeded.&lt;/p&gt;

&lt;p&gt;The final operational counters also showed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;zero pending handoffs;&lt;/li&gt;
&lt;li&gt;zero handoff queue depth;&lt;/li&gt;
&lt;li&gt;zero handoff failures, stale acknowledgements, or queue-full events;&lt;/li&gt;
&lt;li&gt;zero remaining migrations;&lt;/li&gt;
&lt;li&gt;zero universe-sync errors;&lt;/li&gt;
&lt;li&gt;zero global retrieval requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last point is important for KoutenDB's design. The workload used ring-scoped retrieval, and the metrics confirmed that it did not silently fall back to a cluster-wide retrieval path.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why this is more useful than a startup smoke test
&lt;/h2&gt;

&lt;p&gt;A startup test can show that a server accepts a request. It does not show what happens after hundreds of thousands of persistent writes, repeated read paths, periodic metrics calls, topology bookkeeping, and a full stop-and-reopen boundary.&lt;/p&gt;

&lt;p&gt;This run specifically exercised the persistent cluster path over time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;records were written to disk-backed nodes;&lt;/li&gt;
&lt;li&gt;later requests read data that had been accumulated by the same run;&lt;/li&gt;
&lt;li&gt;cluster handoff and migration counters were observed throughout the run;&lt;/li&gt;
&lt;li&gt;the servers were stopped cleanly;&lt;/li&gt;
&lt;li&gt;the on-disk state was opened and verified offline.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It does establish a concrete baseline: KoutenDB can sustain a mixed, persistent local cluster workload for three days without a client-visible error or an offline integrity failure.&lt;/p&gt;
&lt;h2&gt;
  
  
  What changed in v0.10.0
&lt;/h2&gt;

&lt;p&gt;The endurance runner is one part of a larger operational hardening release.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Operational configuration can now be loaded and verified before a server is
used.&lt;/li&gt;
&lt;li&gt;Write guardrails, audit events, capacity thresholds, and backup verification
provide explicit operational checks.&lt;/li&gt;
&lt;li&gt;Explicit scale-in migration and rolling topology activation add controlled
drain, handoff, and progress behavior.&lt;/li&gt;
&lt;li&gt;Cluster retrieval now remains in the requested ring rather than falling back
to a cluster-wide scan.&lt;/li&gt;
&lt;li&gt;Physical placement is decoupled from the logical orbit schedule, so a
logical orbit boundary does not stall local request processing.&lt;/li&gt;
&lt;li&gt;Handoff I/O is kept off the request loop, and stale handoff replay cannot
resurrect an older mutation.&lt;/li&gt;
&lt;li&gt;The optional FAISS backend was removed. Vector retrieval now follows the
core KoutenDB model directly: select a ring first, then perform exact cosine
ranking over that bounded candidate set.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are not unrelated features. They move the project from a collection of locality experiments toward an operable persistent service: know what the server is configured to do, constrain unsafe operation, observe background state, and verify the data after a long workload.&lt;/p&gt;
&lt;h2&gt;
  
  
  Reproducing the run
&lt;/h2&gt;

&lt;p&gt;The runner is included in the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;KOUTEN_SOAK_SECONDS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;259200 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;KOUTEN_SOAK_WORKDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/koutendb-soak-72h &lt;span class="se"&gt;\&lt;/span&gt;
examples/soak_72h.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It writes JSON Lines progress, node logs, final metrics, a final snapshot, offline verification output, the workload configuration, and a &lt;code&gt;completed.ok&lt;/code&gt; marker only after all final verification steps succeed.&lt;/p&gt;

&lt;p&gt;The full configuration and scope are documented here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/puffball1567/koutendb/blob/main/docs/soak-testing.md" rel="noopener noreferrer"&gt;https://github.com/puffball1567/koutendb/blob/main/docs/soak-testing.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The run is intentionally not a CI job. A three-day endurance test is useful because it is long enough to observe persistent operational behavior; it would make normal pull-request feedback unusably slow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The next reliability question
&lt;/h2&gt;

&lt;p&gt;This result validates the current local, disk-backed, buffered-durability cluster mode. The next endurance variants are clear: strong-durability writes, TLS and authenticated transport, Docker/container deployment, and eventually multiple machines.&lt;/p&gt;

&lt;p&gt;Those tests extend this baseline rather than replace it. The v0.10.0 result is now a documented, reproducible reference point for future changes to storage, handoff, topology, and retrieval behavior.&lt;/p&gt;

&lt;p&gt;Source:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/puffball1567/koutendb" rel="noopener noreferrer"&gt;https://github.com/puffball1567/koutendb&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>opensource</category>
      <category>nim</category>
      <category>devlog</category>
    </item>
    <item>
      <title>Clay Board Style System update: Link Navigation</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Mon, 03 Aug 2026 11:52:50 +0000</pubDate>
      <link>https://dev.to/puffball1567/clay-board-style-system-update-link-navigation-49gj</link>
      <guid>https://dev.to/puffball1567/clay-board-style-system-update-link-navigation-49gj</guid>
      <description>&lt;p&gt;Clay Board Style System now provides typed native Link Navigation.&lt;/p&gt;

&lt;p&gt;For readability, I will refer to Clay Board Style System as &lt;strong&gt;CBSS&lt;/strong&gt; below.&lt;br&gt;
CBSS is only an abbreviation used in this article; the project's official name&lt;br&gt;
is Clay Board Style System.&lt;/p&gt;

&lt;p&gt;The feature lets a native application move between screens with a flow similar&lt;br&gt;
to a Single-Page Application: the window remains open, the navigation menu&lt;br&gt;
remains in place, and only the active screen changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fybk4il5zie6v1cai8n84.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fybk4il5zie6v1cai8n84.gif" alt="Clay Board Style System navigation demo" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, we will build a small navigation menu with three destinations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Home&lt;/li&gt;
&lt;li&gt;Projects&lt;/li&gt;
&lt;li&gt;Settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clicking a menu item switches the visible screen inside the same native SDL3&lt;br&gt;
window. No DOM, browser history, WebView, or URL-string router is required.&lt;/p&gt;
&lt;h2&gt;
  
  
  Run the finished demo
&lt;/h2&gt;

&lt;p&gt;The complete interactive demo is already included in the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/puffball1567/clay-board-style-system.git
&lt;span class="nb"&gt;cd &lt;/span&gt;clay-board-style-system
nimble setupBundled
nimble navigationDemo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full source is available in&lt;br&gt;
&lt;a href="https://github.com/puffball1567/clay-board-style-system/blob/main/examples/navigation_demo.nim" rel="noopener noreferrer"&gt;&lt;code&gt;examples/navigation_demo.nim&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1: Define the screens
&lt;/h2&gt;

&lt;p&gt;Start by describing the destinations as a normal Nim enum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;clay_board_style_system&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Screen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt;
  &lt;span class="n"&gt;homeScreen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;projectsScreen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;settingsScreen&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;navigator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initStackNavigator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;homeScreen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;navigator&lt;/code&gt; now owns the screen history. The initial destination is&lt;br&gt;
&lt;code&gt;homeScreen&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Because destinations are Nim values, invalid destinations are caught by the&lt;br&gt;
compiler instead of becoming malformed route strings at runtime.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2: Define a small set of styles
&lt;/h2&gt;

&lt;p&gt;The navigation behavior does not impose a visual design. These styles create a&lt;br&gt;
simple sidebar and content area:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;proc &lt;/span&gt;&lt;span class="nf"&gt;appStyle&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="n"&gt;UiStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;uiStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;520&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"flex-direction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"row"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"background-color"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colorValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.04&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.08&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="k"&gt;proc &lt;/span&gt;&lt;span class="nf"&gt;menuStyle&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="n"&gt;UiStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;uiStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;220&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;520&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"padding"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gap"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"flex-direction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"column"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"background-color"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colorValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.07&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.09&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.13&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="k"&gt;proc &lt;/span&gt;&lt;span class="nf"&gt;contentStyle&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="n"&gt;UiStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;uiStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;680&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;520&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"relative"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"overflow"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hidden"&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="k"&gt;proc &lt;/span&gt;&lt;span class="nf"&gt;screenStyle&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="n"&gt;UiStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;uiStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"absolute"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"left"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&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;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"top"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&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;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;680&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;520&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"padding"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"gap"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"flex-direction"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"column"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"background-color"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colorValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.04&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.08&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="k"&gt;proc &lt;/span&gt;&lt;span class="nf"&gt;linkStyle&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="n"&gt;UiStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;uiStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"width"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"padding"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"align-items"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"center"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"background-color"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colorValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.21&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"border-radius"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cursor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pointer"&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="k"&gt;proc &lt;/span&gt;&lt;span class="nf"&gt;linkTextStyle&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="n"&gt;UiStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;uiStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"font-size"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"line-height"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;decl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"color"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colorValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rgb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.91&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.94&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.98&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;These are ordinary CBSS styles. A component library can replace all of them&lt;br&gt;
without changing the navigation code.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3: Build the menu and screens
&lt;/h2&gt;

&lt;p&gt;Keep the three screen roots so they can be registered with the navigator. We&lt;br&gt;
also keep the Back and Forward buttons for the next step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;AppView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt;
  &lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NodeHandle&lt;/span&gt;
  &lt;span class="n"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NodeHandle&lt;/span&gt;
  &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NodeHandle&lt;/span&gt;
  &lt;span class="n"&gt;backButton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ButtonHandle&lt;/span&gt;
  &lt;span class="n"&gt;forwardButton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ButtonHandle&lt;/span&gt;
  &lt;span class="n"&gt;refreshProjects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ButtonHandle&lt;/span&gt;
  &lt;span class="n"&gt;notifications&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CheckboxHandle&lt;/span&gt;

&lt;span class="k"&gt;proc &lt;/span&gt;&lt;span class="nf"&gt;buildView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UiRoot&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Navigator&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Screen&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;AppView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appStyle&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
    &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;menuStyle&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
      &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"My application"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;homeScreen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"Home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;linkStyle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;textStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;linkTextStyle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;projectsScreen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"Projects"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;linkStyle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;textStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;linkTextStyle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;settingsScreen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"Settings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;linkStyle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;textStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;linkTextStyle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Back"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forwardButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Forward"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contentStyle&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
      &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;screenStyle&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
        &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Home"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Welcome to the native application."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;screenStyle&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
        &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Projects"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Project data will be displayed here."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;refreshProjects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Refresh projects"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;screenStyle&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
        &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Settings"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notifications&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
          &lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;checkbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Enable notifications"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hierarchy is visible directly in the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;application
├── navigation menu
│   ├── Home Link
│   ├── Projects Link
│   ├── Settings Link
│   ├── Back button
│   └── Forward button
└── content area
    ├── Home screen
    ├── Projects screen
    └── Settings screen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ui.link()&lt;/code&gt; is a style-neutral native Link. It handles pointer activation,&lt;br&gt;
Enter-key activation, keyboard focus, disabled state, and accessibility&lt;br&gt;
semantics.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 4: Register the screens
&lt;/h2&gt;

&lt;p&gt;Build the view once, then associate each typed destination with its screen root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ui&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initUiRoot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;navigator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initStackNavigator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;homeScreen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;buildView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initNavigationScreenHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;registerScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;homeScreen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;registerScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectsScreen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;registerScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settingsScreen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;settings&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;interaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initInteractionState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;newException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"initial screen could not be activated"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, only Home is active.&lt;/p&gt;

&lt;p&gt;When a Link receives a click or Enter-key event, it updates the navigator. After&lt;br&gt;
the current platform-event batch, synchronize the screen host:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;screenChanged&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In a real application this line belongs in the central event loop, after CBSS&lt;br&gt;
has dispatched the current batch of SDL3 events. The complete demo shows that&lt;br&gt;
integration without hiding it behind tutorial pseudocode.&lt;/p&gt;

&lt;p&gt;Only the active screen participates in layout, painting, hit testing, keyboard&lt;br&gt;
focus, and the visible accessibility tree. The other screens remain retained&lt;br&gt;
but inert.&lt;/p&gt;

&lt;p&gt;That is the SPA-like part: switching screens does not reconstruct the complete&lt;br&gt;
application tree or open another native window.&lt;/p&gt;
&lt;h2&gt;
  
  
  Add Back and Forward
&lt;/h2&gt;

&lt;p&gt;The same navigator provides history operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DispatchResult&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;back&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s"&gt;"Already at the first screen"&lt;/span&gt;
  &lt;span class="kp"&gt;true&lt;/span&gt;

&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forwardButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DispatchResult&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s"&gt;"Already at the latest screen"&lt;/span&gt;
  &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;push&lt;/code&gt;, &lt;code&gt;replace&lt;/code&gt;, &lt;code&gt;back&lt;/code&gt;, and &lt;code&gt;forward&lt;/code&gt; operate on typed history entries. Two&lt;br&gt;
visits to Projects remain two separate entries, which allows CBSS to restore&lt;br&gt;
focus for the correct visit when navigating through history.&lt;/p&gt;
&lt;h2&gt;
  
  
  Request data with Joubako
&lt;/h2&gt;

&lt;p&gt;Navigation belongs to CBSS, while HTTP communication belongs to the&lt;br&gt;
application's data layer.&lt;/p&gt;

&lt;p&gt;I am also developing&lt;br&gt;
&lt;a href="https://github.com/puffball1567/joubako" rel="noopener noreferrer"&gt;&lt;code&gt;Joubako&lt;/code&gt;&lt;/a&gt;, a separate async transport&lt;br&gt;
client for Nim. It supports HTTP(S), typed JSON, standard Nim &lt;code&gt;await&lt;/code&gt;,&lt;br&gt;
result-aware callback composition, WebSockets, local IPC, and optional NIF/BIF&lt;br&gt;
data exchange.&lt;/p&gt;

&lt;p&gt;It can be started from a normal CBSS event handler without coupling networking&lt;br&gt;
to the navigation system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;asyncdispatch&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;joubako&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newHttpTransport&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"https://api.example.com/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;proc &lt;/span&gt;&lt;span class="nf"&gt;loadProjects&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;async&lt;/span&gt;&lt;span class="p"&gt;.}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;outcome&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"projects"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;seq&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Project&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isErr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s"&gt;"Request failed: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;

  &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s"&gt;"Loaded projects: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;outcome&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="n"&gt;len&lt;/span&gt;
  &lt;span class="c"&gt;# Update application state and mark the affected view dirty here.&lt;/span&gt;

&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;refreshProjects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DispatchResult&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="n"&gt;asyncCheck&lt;/span&gt; &lt;span class="n"&gt;loadProjects&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Joubako is not bundled with CBSS and is not required for Link Navigation. An&lt;br&gt;
application can use Joubako, another HTTP client, IPC, an FFI service, or a&lt;br&gt;
fully local data source behind the same event-handler boundary.&lt;/p&gt;
&lt;h2&gt;
  
  
  Optional features
&lt;/h2&gt;

&lt;p&gt;The basic menu above is enough for normal in-process screen navigation. CBSS&lt;br&gt;
also provides optional APIs for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;focus restoration for each history entry;&lt;/li&gt;
&lt;li&gt;animated screen transitions;&lt;/li&gt;
&lt;li&gt;validated application deep links;&lt;/li&gt;
&lt;li&gt;replacing one retained screen without rebuilding the application; and&lt;/li&gt;
&lt;li&gt;injecting a custom navigation driver.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These features are documented in the&lt;br&gt;
&lt;a href="https://github.com/puffball1567/clay-board-style-system/blob/main/docs/navigation.md" rel="noopener noreferrer"&gt;&lt;code&gt;Native Navigation guide&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why use typed destinations?
&lt;/h2&gt;

&lt;p&gt;A browser URL is useful when the URL is the application's public address. A&lt;br&gt;
native in-process screen does not always need that string boundary.&lt;/p&gt;

&lt;p&gt;With CBSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="n"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectsScreen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is checked against &lt;code&gt;Screen&lt;/code&gt; by Nim. A larger application can replace the enum&lt;br&gt;
with a variant object that carries typed parameters, such as a project ID.&lt;/p&gt;

&lt;p&gt;External deep links can still be decoded into the same destination type at the&lt;br&gt;
edge of the application. Internal navigation remains typed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current status
&lt;/h2&gt;

&lt;p&gt;Clay Board Style System v0.3 is a developer preview. Linux x86_64 with SDL3 is&lt;br&gt;
currently the Tier 1 runtime. Windows and macOS portable builds run in CI, while&lt;br&gt;
complete runtime validation on those platforms remains contributor-driven.&lt;/p&gt;

&lt;p&gt;Link Navigation is covered by unit tests, retained-screen tests, transition and&lt;br&gt;
focus tests, performance checks, and a real SDL3/Wayland E2E scenario.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/clay-board-style-system" rel="noopener noreferrer"&gt;Clay Board Style System&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/clay-board-style-system/blob/main/docs/navigation.md" rel="noopener noreferrer"&gt;Native Navigation guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/clay-board-style-system/blob/main/examples/navigation_demo.nim" rel="noopener noreferrer"&gt;Complete navigation demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/clay-board-style-system/releases/tag/v0.3.0" rel="noopener noreferrer"&gt;Clay Board Style System v0.3.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/puffball1567/joubako" rel="noopener noreferrer"&gt;Joubako&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feedback on the Link API and the navigation-menu authoring experience is very&lt;br&gt;
welcome.&lt;/p&gt;

</description>
      <category>nim</category>
      <category>gui</category>
      <category>native</category>
      <category>opensource</category>
    </item>
    <item>
      <title>RAG Retrieval Optimization: Reduce Vector Search Before Ranking</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Mon, 03 Aug 2026 04:21:03 +0000</pubDate>
      <link>https://dev.to/puffball1567/rag-retrieval-optimization-reduce-vector-search-before-ranking-5h16</link>
      <guid>https://dev.to/puffball1567/rag-retrieval-optimization-reduce-vector-search-before-ranking-5h16</guid>
      <description>&lt;p&gt;Most RAG performance advice begins at the ranking stage: choose a faster embedding model, tune an ANN index, reduce the result count, add a reranker, or cache common queries.&lt;/p&gt;

&lt;p&gt;Those are useful techniques. But RAG retrieval optimization should start one step earlier:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why is this query considering these vectors at all?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A request often already contains a reliable boundary: tenant, repository, product, language, document type, version, date range, permission scope, or the object currently open in the application. Applying that knowledge before vector ranking can reduce RAG latency, vector-search memory use, and unrelated context.&lt;/p&gt;

&lt;p&gt;This is the difference between searching a whole corpus for similar documents and searching the authorized, relevant part of that corpus. It is also the specific problem that a locality-aware RAG database such as&lt;br&gt;
&lt;a href="https://github.com/puffball1567/koutendb" rel="noopener noreferrer"&gt;KoutenDB&lt;/a&gt; explores.&lt;/p&gt;
&lt;h2&gt;
  
  
  Vector search metadata filtering is not one operation
&lt;/h2&gt;

&lt;p&gt;Vector search metadata filtering commonly restricts a query by fields such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tenant_id = "acme"
AND product = "billing"
AND language = "en"
AND published = true
AND version = "2026.2"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those constraints are necessary for both relevance and security. Their placement in the read path, however, changes the cost:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Filter after broad retrieval&lt;/td&gt;
&lt;td&gt;Rank widely, then discard ineligible candidates.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filter-aware vector index&lt;/td&gt;
&lt;td&gt;Use indexed metadata while selecting vector candidates.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Namespace or partition selection&lt;/td&gt;
&lt;td&gt;Select an eligible subset, then search it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application-local routing&lt;/td&gt;
&lt;td&gt;Route directly to a known tenant, source, or related-data neighborhood.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The first approach can still enforce correct access rules, but it may score vectors that should never have been candidates. The latter two are valuable when the application already knows a stable boundary.&lt;/p&gt;

&lt;p&gt;For a multi-tenant support assistant, the tenant boundary is not merely a relevance hint. It is an authorization rule and a natural first search scope.&lt;br&gt;
For a code assistant, the active repository and branch often serve the same purpose. For a product assistant, version and language can rule out most of the corpus before similarity becomes useful.&lt;/p&gt;
&lt;h2&gt;
  
  
  RAG optimization starts with the working set
&lt;/h2&gt;

&lt;p&gt;The working set is everything a request touches: candidate vectors, metadata,payload bytes, reranker inputs, and finally model context. Reducing it changes several costs at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fewer vectors to score;&lt;/li&gt;
&lt;li&gt;less vector memory to read;&lt;/li&gt;
&lt;li&gt;fewer payloads to filter and project;&lt;/li&gt;
&lt;li&gt;fewer candidates for a reranker;&lt;/li&gt;
&lt;li&gt;less irrelevant context competing for the prompt budget.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not an argument for creating a partition for every tag. A pre-ranking boundary should be stable, known at request time, meaningful to the application,and strong enough to exclude large amounts of unrelated data. Tenant, source,product, and document-version boundaries often qualify; free-form tags usually do not.&lt;/p&gt;

&lt;p&gt;There is also an essential quality check: a smaller scope is only an optimization if it retains the documents the user needs. A wrong partition can be fast because it is wrong.&lt;/p&gt;
&lt;h2&gt;
  
  
  KoutenDB: locality first, exact vector ranking second
&lt;/h2&gt;

&lt;p&gt;KoutenDB is an open-source, embedded-capable document and vector database written in Nim. It is not a general replacement for PostgreSQL, a mature ANN vector database, or a global secondary-index engine.&lt;/p&gt;

&lt;p&gt;Its vector path makes a focused trade-off:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the application chooses a ring, a semantic locality boundary;&lt;/li&gt;
&lt;li&gt;KoutenDB retrieves candidates from that ring;&lt;/li&gt;
&lt;li&gt;it performs dependency-free exact cosine ranking over that bounded set.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, a RAG application that already knows the tenant and product can place and query documentation in a corresponding ring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nim"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;koutendb&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;koutendb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;@[&lt;/span&gt;&lt;span class="mf"&gt;1.0'f32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0'f32&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ring&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"tenant/acme/product/billing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;budget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ring is chosen by application logic, policy, or an import rule; KoutenDB does not claim to infer the right security or business boundary from embedding similarity. JSONL ingestion can derive rings from a field, for example with a tenant field and a tenant prefix.&lt;/p&gt;

&lt;p&gt;Filters and projections still narrow results, but only after the local ring has been selected. This boundary is important: KoutenDB is a good fit when an application can name a meaningful pre-ranking scope. It is not the right primary tool when every query must perform global, cross-corpus discovery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure vectors scanned, not only results returned
&lt;/h2&gt;

&lt;p&gt;Returning fewer hits does not prove that a system did less search work. A useful RAG database should expose whether unrelated candidates were skipped before ranking.&lt;/p&gt;

&lt;p&gt;KoutenDB reports total vectors, scanned candidates, skipped vectors, rings touched, candidate reduction, payload bytes, and estimated tokens. Its included working-set benchmark uses 10,000 vectors across 100 rings. In one local run, global retrieval scanned 10,000 vectors per query, while routed retrieval scanned 100: a 99% reduction. Measured latency in that run was 1,954.9 microseconds for the global path and 31.4 microseconds for the routed path.&lt;/p&gt;

&lt;p&gt;An included RAG-style test kept recall at 1.000 for the correctly routed ring while reducing scanned candidates from 400 to 40 and estimated tokens from 615.2 to 231.6. It also demonstrates the failure case: selecting the wrong ring keeps the small scan but produces zero recall.&lt;/p&gt;

&lt;p&gt;These are local synthetic measurements, not a universal latency claim or a claim that KoutenDB beats every vector database. They demonstrate a narrower invariant: candidates in unrelated rings are skipped before exact vector scoring, rather than discarded after a broad search.&lt;/p&gt;

&lt;h2&gt;
  
  
  When locality-first retrieval is useful
&lt;/h2&gt;

&lt;p&gt;This approach is particularly relevant for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multi-tenant RAG and tenant-based data isolation;&lt;/li&gt;
&lt;li&gt;support systems scoped to a customer, product, or deployment;&lt;/li&gt;
&lt;li&gt;code assistants scoped to a repository or service;&lt;/li&gt;
&lt;li&gt;documentation assistants with known language and version;&lt;/li&gt;
&lt;li&gt;local AI or desktop applications with a bounded local corpus;&lt;/li&gt;
&lt;li&gt;systems that need nearby application history as well as similar chunks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is less suitable when global discovery is the core job, or when the application cannot identify a trustworthy boundary before retrieval. In that case, a broad ANN-oriented vector database with filter-aware indexing is often the better primary retrieval layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  A checklist for RAG retrieval optimization
&lt;/h2&gt;

&lt;p&gt;Before tuning a model or index, ask:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Which tenant, permission, source, version, or task boundary is known before retrieval?&lt;/li&gt;
&lt;li&gt;Is that boundary enforced before candidates are ranked?&lt;/li&gt;
&lt;li&gt;How many vectors are scanned, rather than merely returned?&lt;/li&gt;
&lt;li&gt;Does scoped retrieval preserve recall on representative queries?&lt;/li&gt;
&lt;li&gt;Can the system explain why each document was eligible?&lt;/li&gt;
&lt;li&gt;Are payload projection and context budgets applied before the LLM call?&lt;/li&gt;
&lt;li&gt;Can the same routing rules be validated offline?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The answer may lead to namespaces, metadata indexes, database partitions, or a locality-aware database. The common requirement is to make the pre-ranking boundary explicit and measurable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The best way to reduce RAG cost or RAG latency is not always a more complicated ranking algorithm. Often it is avoiding a ranking problem that the application already knows is irrelevant.&lt;/p&gt;

&lt;p&gt;Use metadata filtering for correctness. Use tenant, source, version, and task locality to reduce the eligible search space when those boundaries are trustworthy. Then measure scanned candidates, recall, latency, and context size together.&lt;/p&gt;

&lt;p&gt;That is the design space KoutenDB explores: a database for RAG where application-level locality becomes part of the retrieval path before exact vector ranking begins.&lt;/p&gt;

</description>
      <category>rag</category>
      <category>database</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
