<?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>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;

</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%2Cformat=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%2Cformat=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%2Cformat=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>
    <item>
      <title>KoutenDB v0.9.0: From a Locality Experiment to a Testable Database</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Sun, 02 Aug 2026 11:09:04 +0000</pubDate>
      <link>https://dev.to/puffball1567/koutendb-v090-from-a-locality-experiment-to-a-testable-database-2api</link>
      <guid>https://dev.to/puffball1567/koutendb-v090-from-a-locality-experiment-to-a-testable-database-2api</guid>
      <description>&lt;p&gt;I released &lt;strong&gt;KoutenDB v0.9.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.9.0" rel="noopener noreferrer"&gt;https://github.com/puffball1567/koutendb/releases/tag/v0.9.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;KoutenDB is a ring-oriented NoSQL document and vector database written in Nim.&lt;br&gt;
Its central idea is deliberately narrow: when an application already knows a useful locality boundary, that knowledge should reduce the data a request has to inspect.&lt;/p&gt;

&lt;p&gt;Previous releases established the data model, public API, native C ABI, and language-driver foundation. v0.9.0 concentrates on a harder question: can that locality model be tested against a realistic, persistent related-data read shape rather than only described as a design idea?&lt;/p&gt;

&lt;p&gt;This is still a technical preview. It is not a claim that KoutenDB replaces PostgreSQL, Redis, or every document store. The goal of this release is to make the narrower claim measurable and reproducible.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Request Shape: One Entity, Several Bounded Collections
&lt;/h2&gt;

&lt;p&gt;Many web application detail pages need more than one record. A user page, for example, may need a profile, a few addresses, recent employment entries, preferences, recent orders, and notifications. Each collection has its own limit and sort order.&lt;/p&gt;

&lt;p&gt;In a relational database, this can be expressed with several indexed queries or with a JSON aggregate query built from limited subqueries. Both are valid approaches. The question KoutenDB explores is different: if these collections are already known to belong to one user, can that relationship be represented as bounded nearby data from the start?&lt;/p&gt;

&lt;p&gt;v0.9.0 models the example as subrings below a user ring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kouten get &lt;span class="nt"&gt;--ring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;users&lt;/span&gt;/&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;profile,addresses,career,preferences,orders,notifications &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subring-limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;profile:1,addresses:3,career:2,preferences:1,orders:10,notifications:5 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subring-rsort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;orders:time,notifications:time
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This says exactly what the endpoint needs: retrieve six nearby collections, with independent bounds. It is not a general query language and it is not intended to be one. The useful property is that the caller supplies the locality boundary before the read begins.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Changed in v0.9.0
&lt;/h2&gt;

&lt;p&gt;The release adds a benchmark for that heterogeneous related-data bundle and strengthens the corresponding read path.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;readStellar&lt;/code&gt; now prepares projection state once and reuses it across
subrings.&lt;/li&gt;
&lt;li&gt;Stellar reads validate per-subring limits and descending time sorts.&lt;/li&gt;
&lt;li&gt;Simple embedded reads with an empty filter, a positive limit, and &lt;code&gt;id&lt;/code&gt; or
&lt;code&gt;time&lt;/code&gt; ordering can use a bounded ring-window read path.&lt;/li&gt;
&lt;li&gt;Disk-backed segment reads reuse cached streams instead of repeatedly opening
the same segment files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;import-jsonl&lt;/code&gt; supports chunked commits through &lt;code&gt;--batch-size=N&lt;/code&gt; for larger
data imports.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point of these changes is not an isolated micro-optimization. A locality model only helps if its normal persistent read path remains bounded when the dataset grows.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Reproducible Related-Data Comparison
&lt;/h2&gt;

&lt;p&gt;The repository now includes a helper that builds fresh temporary KoutenDB and PostgreSQL data directories, loads the same logical user dataset, and measures the related-data bundle:&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;N&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10000 &lt;span class="nv"&gt;READS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1000 examples/subring_bundle_postgres_bench.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One local run on 2026-07-21 used an AMD Ryzen 5 5600H, Linux 6.8, Nim 2.2.10, and PostgreSQL 14.23.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Users&lt;/th&gt;
&lt;th&gt;Logical records&lt;/th&gt;
&lt;th&gt;System and query shape&lt;/th&gt;
&lt;th&gt;Returned records&lt;/th&gt;
&lt;th&gt;Read latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;1,050,000&lt;/td&gt;
&lt;td&gt;KoutenDB &lt;code&gt;users/&amp;lt;id&amp;gt;/*&lt;/code&gt; stellar read with per-subring bounds&lt;/td&gt;
&lt;td&gt;22 across 6 rings&lt;/td&gt;
&lt;td&gt;196.859 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;1,050,000&lt;/td&gt;
&lt;td&gt;PostgreSQL: six indexed &lt;code&gt;SELECT&lt;/code&gt; statements&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;515 us&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;1,050,000&lt;/td&gt;
&lt;td&gt;PostgreSQL: JSON aggregate over indexed limited subqueries&lt;/td&gt;
&lt;td&gt;1 JSON bundle&lt;/td&gt;
&lt;td&gt;236 us&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These numbers describe one machine and one workload; they are not a universal database ranking. PostgreSQL's aggregate form is close to the measured KoutenDB result, and it expresses the result through a different but perfectly&lt;br&gt;
reasonable abstraction. The value of the comparison is to keep the claim honest: KoutenDB's ring and subring model should be judged on workloads where the application can name a local working set.&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing the Larger Locality Claim
&lt;/h2&gt;

&lt;p&gt;v0.9.0 also records the completed disk-backed effect-validation run on generated data. The validation imports deterministic JSONL corpora and compares broad retrieval with ring-routed retrieval. It reports import time, records scanned, estimated token volume, and retrieval latency.&lt;/p&gt;

&lt;p&gt;The standard scale-1000 matrix completed locally with a largest case of 13,500,000 documents.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workload&lt;/th&gt;
&lt;th&gt;Broad scan&lt;/th&gt;
&lt;th&gt;Ring-routed scan&lt;/th&gt;
&lt;th&gt;Estimated tokens: broad -&amp;gt; routed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;small-balanced&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;168,000&lt;/td&gt;
&lt;td&gt;24,000&lt;/td&gt;
&lt;td&gt;692 -&amp;gt; 260&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;near-distractors&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1,860,000&lt;/td&gt;
&lt;td&gt;120,000&lt;/td&gt;
&lt;td&gt;1,730 -&amp;gt; 433&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;medium-noisy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;13,500,000&lt;/td&gt;
&lt;td&gt;500,000&lt;/td&gt;
&lt;td&gt;2,595 -&amp;gt; 692&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The token column is an estimate for the generated retrieval payloads, not a benchmark of any model. More generally, this validation is not limited to AI workloads. Scanned records, transferred data, candidate memory, and downstream application work all increase when a request must inspect unrelated data.&lt;/p&gt;

&lt;p&gt;The important result is that the test makes the expected trade-off visible:&lt;br&gt;
locality can reduce the candidate set when the application supplies a valid ring boundary. It cannot invent a useful boundary when the application has none.&lt;/p&gt;
&lt;h2&gt;
  
  
  Offline Validation Before Production Traffic
&lt;/h2&gt;

&lt;p&gt;The release also adds an offline JSONL path:&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_REAL_JSONL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/path/to/export.jsonl &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;QUERY_RING&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;docs/japan &lt;span class="se"&gt;\&lt;/span&gt;
examples/offline_effect_validation.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is intended for copied or exported data rather than a production service.&lt;br&gt;
It gives a team a way to test whether a proposed ring layout narrows its own workload before asking the database to serve live traffic.&lt;/p&gt;

&lt;p&gt;That boundary matters. A database experiment should be reproducible without requiring production credentials, traffic capture, or an unbounded benchmark environment.&lt;/p&gt;
&lt;h2&gt;
  
  
  Verification Included in the Release
&lt;/h2&gt;

&lt;p&gt;The v0.9.0 release state was checked with Nim module checks, the public API test program, the smoke suite, package validation, and whitespace validation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nim check src/kouten/store.nim
nim check src/koutendb.nim
nim check src/koutencli.nim
nim check tests/tapi.nim
nim c &lt;span class="nt"&gt;--nimcache&lt;/span&gt;:/tmp/nimcache_kouten_tapi &lt;span class="nt"&gt;-r&lt;/span&gt; tests/tapi.nim
scripts/test_all_smoke.sh
nimble check
git diff &lt;span class="nt"&gt;--check&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The largest generated run remains an explicit manual validation rather than a default CI job. It is valuable precisely because it exercises a scale that a quick smoke test should not pretend to cover.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Comes Next
&lt;/h2&gt;

&lt;p&gt;v0.9.0 makes the locality hypothesis easier to test with persistent data and a concrete web-style read shape. The next work is about making that evaluation more operable: verification commands, controlled drain and snapshot workflows, backup checks, audit records, and safer topology transitions.&lt;/p&gt;

&lt;p&gt;Those are v0.10 development goals, not claims included in v0.9.0.&lt;/p&gt;

&lt;p&gt;KoutenDB remains an early project, but the direction is now more concrete:&lt;br&gt;
data locality should be a testable part of a database read path, not only a diagram or a benchmark headline.&lt;/p&gt;

&lt;p&gt;Source and release notes:&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>rag</category>
    </item>
    <item>
      <title>Introducing Clay Board Style System: CSS-Inspired Native UI for Nim</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Sun, 02 Aug 2026 10:59:52 +0000</pubDate>
      <link>https://dev.to/puffball1567/introducing-clay-board-style-system-css-inspired-native-ui-for-nim-346a</link>
      <guid>https://dev.to/puffball1567/introducing-clay-board-style-system-css-inspired-native-ui-for-nim-346a</guid>
      <description>&lt;p&gt;I am releasing the first developer preview of &lt;strong&gt;Clay Board Style System&lt;/strong&gt;, a&lt;br&gt;
CSS-inspired primitive engine for native GUI toolkits written in Nim.&lt;/p&gt;

&lt;p&gt;For brevity, this article refers to Clay Board Style System as &lt;strong&gt;CBSS&lt;/strong&gt; after&lt;br&gt;
this point. CBSS is only an abbreviation used in writing; the project's&lt;br&gt;
official name is Clay Board Style System.&lt;/p&gt;

&lt;p&gt;Clay Board Style System starts from a simple observation: Web frontend work is&lt;br&gt;
productive partly because CSS gives many frameworks a shared vocabulary for&lt;br&gt;
layout and visual design. Native GUI development often has to rebuild that&lt;br&gt;
foundation separately for every toolkit.&lt;/p&gt;

&lt;p&gt;The project is an attempt to provide that primitive layer for native&lt;br&gt;
applications without embedding a browser or recreating the Web platform.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fupqbn7vv4q1qwu79t1p5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fupqbn7vv4q1qwu79t1p5.gif" alt="Clay Board Style System demo: native controls, text input, scrolling, and styled layouts" width="760" height="613"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What Clay Board Style System is
&lt;/h2&gt;

&lt;p&gt;Clay Board Style System provides a tree of primitive &lt;code&gt;Box&lt;/code&gt;, &lt;code&gt;Text&lt;/code&gt;, and &lt;code&gt;Image&lt;/code&gt;&lt;br&gt;
nodes, along with typed styles, layout, paint commands, hit testing, focus,&lt;br&gt;
input dispatch, retained scrolling, and accessibility semantics.&lt;/p&gt;

&lt;p&gt;The authoring model intentionally uses familiar CSS ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;spacing, borders, radii, colors, and typography;&lt;/li&gt;
&lt;li&gt;row and column layout, alignment, sizing, clipping, and scrolling;&lt;/li&gt;
&lt;li&gt;absolute and overlay positioning;&lt;/li&gt;
&lt;li&gt;state styling for hover, active, focus, and disabled states.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The aim is that someone who can read a Web design article or a CSS example can&lt;br&gt;
bring much of its layout and visual reasoning to a native application.&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;"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;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;"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;"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.10&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="mf"&gt;0.16&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;16&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;"A native UI, styled with familiar ideas"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What it is not
&lt;/h2&gt;

&lt;p&gt;Clay Board Style System is not a browser, a WebView, an HTML renderer, or a&lt;br&gt;
full CSS implementation. It does not aim for DOM compatibility, JavaScript,&lt;br&gt;
browser quirks, or bug-for-bug CSS behavior.&lt;/p&gt;

&lt;p&gt;It is also not a finished component library. Buttons, dialogs, menus, inputs,&lt;br&gt;
and editors should be built as compositions on top of the primitive layer.&lt;/p&gt;

&lt;p&gt;This boundary matters. The goal is to reuse the useful language of Web UI&lt;br&gt;
design while keeping a native rendering, input, and application model.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Nim
&lt;/h2&gt;

&lt;p&gt;The first implementation target is Nim because it can produce small native&lt;br&gt;
applications while remaining expressive enough for UI composition. Clay Board&lt;br&gt;
Style System also exposes a versioned C ABI with opaque handles and fixed-size&lt;br&gt;
value structs. That lets an existing C, C++, Rust, Zig, or Swift core be paired&lt;br&gt;
with a Nim frontend, rather than requiring a rewrite before the UI can be&lt;br&gt;
explored.&lt;/p&gt;

&lt;p&gt;My hope is that this creates a practical entry point to Nim: keep the existing&lt;br&gt;
engine or product core, build the frontend in Nim, and adopt Nim more broadly&lt;br&gt;
only where it proves useful.&lt;/p&gt;
&lt;h2&gt;
  
  
  Current developer preview
&lt;/h2&gt;

&lt;p&gt;Version 0.2 targets Linux x86_64 with SDL3. It includes the primitive&lt;br&gt;
style/layout pipeline, text shaping through cosmic-text, event routing, focus,&lt;br&gt;
text input and IME integration, retained scrolling, navigation state, a&lt;br&gt;
semantic accessibility model, and the C ABI.&lt;/p&gt;

&lt;p&gt;The project is still early. Windows and macOS require contributor validation;&lt;br&gt;
platform accessibility transports, animation timelines, full rich-text layout,&lt;br&gt;
and some windowing behavior are not complete. Public APIs may change before&lt;br&gt;
1.0.&lt;/p&gt;

&lt;p&gt;This is therefore a release for people who want to evaluate the approach,&lt;br&gt;
build experiments, and help shape the primitive layer—not a claim that every&lt;br&gt;
desktop application should use it today.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Clay Board Style System requires Nim 2.2 or newer, Rust/Cargo for the native&lt;br&gt;
text and image bridges, and SDL3 development files on the current Linux target.&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 &lt;span class="nb"&gt;test
&lt;/span&gt;nimble sdl3Demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next work is to make the project's visual promise easier to judge: richer&lt;br&gt;
design demos, practical composite controls, and broader platform validation.&lt;br&gt;
Issues, experiments, and feedback are welcome in the repository.&lt;/p&gt;

</description>
      <category>nim</category>
      <category>gui</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>RAG Database Design: SQL, Full-Text Search, Vector Search, and Context Retrieval</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Thu, 23 Jul 2026 14:38:14 +0000</pubDate>
      <link>https://dev.to/puffball1567/rag-database-design-sql-full-text-search-vector-search-and-context-retrieval-9of</link>
      <guid>https://dev.to/puffball1567/rag-database-design-sql-full-text-search-vector-search-and-context-retrieval-9of</guid>
      <description>&lt;p&gt;Retrieval-augmented generation is often introduced with a compact diagram:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;documents -&amp;gt; embeddings -&amp;gt; vector database -&amp;gt; LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a useful starting point, but it can make one part of the architecture&lt;br&gt;
look like the whole system.&lt;/p&gt;

&lt;p&gt;Good RAG database design has to cover more than vector storage.&lt;/p&gt;

&lt;p&gt;A production RAG application may also need to store original documents,&lt;br&gt;
enforce tenant boundaries, find exact product names, filter by date, remember a conversation, invalidate stale content, combine several data sources, and keep the final prompt within a token budget.&lt;/p&gt;

&lt;p&gt;No database becomes a "RAG database" merely by storing embeddings. Different database models solve different parts of retrieval.&lt;/p&gt;

&lt;p&gt;This article looks at the roles of relational databases, document databases, caches, full-text search engines, and vector databases in RAG. It then examines a retrieval problem that is harder to express with similarity alone: finding a useful neighborhood of context when the application knows the center, but not the exact boundary of the answer.&lt;/p&gt;

&lt;p&gt;Among practical AI use cases, this pattern appears in customer support, business-efficiency AI tools for sales, internal knowledge assistants, AI programming automation, and AI agent development. The same retrieval questions also apply to AI assistant development with internal data, where access control and source freshness matter as much as the generated answer.&lt;/p&gt;
&lt;h2&gt;
  
  
  How RAG works—and how it differs from fine-tuning
&lt;/h2&gt;

&lt;p&gt;RAG retrieves external information when a request is made, then places selected evidence into the model's context. Fine-tuning changes model weights. The practical RAG vs fine-tuning differences are therefore about when and where knowledge is introduced.&lt;/p&gt;

&lt;p&gt;RAG is often easier to update, cite, filter, or remove because the knowledge remains outside the model. Fine-tuning can change behavior, style, or learned patterns, but it is not a convenient replacement for a frequently changing document store.&lt;/p&gt;

&lt;p&gt;The main RAG pros and cons follow from that separation. RAG can use current and private sources without retraining the model, but it adds ingestion, retrieval, permissions, evaluation, latency, and operations work. Understanding how AI generation works is not enough: a fluent model can still answer from weak, stale, or unauthorized evidence.&lt;/p&gt;
&lt;h2&gt;
  
  
  Database types and how to build RAG
&lt;/h2&gt;

&lt;p&gt;A more complete RAG pipeline 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;source data
  -&amp;gt; ingestion and normalization
  -&amp;gt; document and metadata storage
  -&amp;gt; candidate selection
  -&amp;gt; lexical or vector ranking
  -&amp;gt; reranking and context assembly
  -&amp;gt; LLM
  -&amp;gt; answer and citations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Databases can participate at several stages.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Responsibility&lt;/th&gt;
&lt;th&gt;Typical data&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Source of truth&lt;/td&gt;
&lt;td&gt;Documents, records, revisions, users, permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ingestion state&lt;/td&gt;
&lt;td&gt;Jobs, checksums, parser versions, failures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exact filtering&lt;/td&gt;
&lt;td&gt;Tenant, category, status, language, date&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lexical retrieval&lt;/td&gt;
&lt;td&gt;Keywords, names, identifiers, error codes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Semantic retrieval&lt;/td&gt;
&lt;td&gt;Embeddings and similarity scores&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Session state&lt;/td&gt;
&lt;td&gt;Recent messages, selected sources, cached results&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context assembly&lt;/td&gt;
&lt;td&gt;Related records, projections, token-bounded payloads&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One database can perform several of these jobs. A small application does not need six databases just because six rows appear in the table. The point is to identify the retrieval job before choosing the storage engine. A useful database comparison begins with those jobs, not with a feature-count table.&lt;/p&gt;

&lt;p&gt;When teams research how to build RAG, they often begin with an embedding model or a RAG vector database. A safer order is to identify the source of truth, access rules, update process, exact filters, candidate retrieval method, and evaluation plan first. The vector index is one component of that design.&lt;/p&gt;

&lt;p&gt;To make the differences concrete, imagine that we are building a travel assistant for Kyoto. It stores information about landmarks, restaurants, cultural facilities, events, transit, opening hours, reservations, and user itineraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  PostgreSQL and MySQL: structured truth and exact relationships
&lt;/h2&gt;

&lt;p&gt;Relational databases are often the least surprising place to keep the authoritative application state around a RAG system.&lt;/p&gt;

&lt;p&gt;For the travel assistant, a relational database could store:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;users and accounts;&lt;/li&gt;
&lt;li&gt;bookings and reservations;&lt;/li&gt;
&lt;li&gt;saved places and itineraries;&lt;/li&gt;
&lt;li&gt;tenant or organization boundaries;&lt;/li&gt;
&lt;li&gt;document versions and ingestion status;&lt;/li&gt;
&lt;li&gt;permissions and visibility rules;&lt;/li&gt;
&lt;li&gt;structured opening hours and accessibility fields.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SQL is especially useful when correctness depends on exact relationships. If a private itinerary belongs to one user, or a document must not cross a tenant boundary, that rule should not depend on an embedding score.&lt;/p&gt;

&lt;p&gt;A relational query can select the allowed document IDs first. The RAG pipeline can then rank only those documents.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;document_id&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;travel_documents&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Kyoto'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;language&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'en'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;published&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;valid_from&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;valid_until&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PostgreSQL also has JSON, full-text search, and extension support. The &lt;a href="https://github.com/pgvector/pgvector" rel="noopener noreferrer"&gt;&lt;code&gt;pgvector&lt;/code&gt;&lt;/a&gt; extension can keep vectors beside relational data, which is often enough for an early or moderately sized RAG application.&lt;/p&gt;

&lt;p&gt;The important distinction is that adding a vector column does not remove the need to design permissions, document lifecycle, metadata, candidate selection, and prompt construction.&lt;/p&gt;

&lt;h2&gt;
  
  
  MongoDB and document databases: flexible content records
&lt;/h2&gt;

&lt;p&gt;A document database is a natural fit when an ingested item already looks like a JSON object and different item types have different fields.&lt;/p&gt;

&lt;p&gt;A landmark record might contain history, images, coordinates, and opening hours. A restaurant may have cuisine, price range, reservation rules, and dietary options. An event may have a start time, venue, ticket URL, and weather policy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"restaurant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Example Cafe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"area"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Higashiyama"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cuisine"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Japanese"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cafe"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"features"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"indoor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"vegetarianOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://www.mongodb.com/docs/manual/core/document/" rel="noopener noreferrer"&gt;MongoDB documents&lt;/a&gt; can&lt;br&gt;
represent that shape without forcing every item type into the same set of columns. Metadata and source text can live together, and nested fields can be indexed when their access patterns are known.&lt;/p&gt;

&lt;p&gt;Flexibility does not eliminate retrieval design, however. The application still has to decide which collection, fields, filters, and records should become candidates for the model.&lt;/p&gt;

&lt;p&gt;The familiar RDB vs NoSQL differences still matter here. Relational systems center exact relationships and constraints; document databases center flexible record shapes and aggregate-oriented access. For business systems, NoSQL database selection criteria should include consistency, authorization, operational skills, backup, and migration—not only whether incoming data is JSON.&lt;/p&gt;
&lt;h2&gt;
  
  
  Redis: short-lived state and repeated work
&lt;/h2&gt;

&lt;p&gt;Redis is useful around RAG even when it is not the primary document store.&lt;/p&gt;

&lt;p&gt;Common uses include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;conversation session state;&lt;/li&gt;
&lt;li&gt;cached retrieval results;&lt;/li&gt;
&lt;li&gt;rate limits;&lt;/li&gt;
&lt;li&gt;short-lived agent state;&lt;/li&gt;
&lt;li&gt;job queues;&lt;/li&gt;
&lt;li&gt;deduplication keys;&lt;/li&gt;
&lt;li&gt;frequently reused context fragments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If many users ask similar questions about the same attraction, caching a validated retrieval result may be cheaper than running the complete pipeline again.&lt;/p&gt;

&lt;p&gt;Redis also supports persistence and several data structures, but a cache still needs an invalidation policy. Travel information makes this obvious: an answer about an event, temporary closure, or opening time can become wrong even when the cached text is internally consistent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://redis.io/docs/latest/develop/get-started/" rel="noopener noreferrer"&gt;Redis documentation&lt;/a&gt;&lt;br&gt;
describes it as more than a simple key/value cache, but its role in a RAG architecture should still be chosen explicitly.&lt;/p&gt;
&lt;h2&gt;
  
  
  OpenSearch: words, fields, and filters still matter
&lt;/h2&gt;

&lt;p&gt;Embedding search is not a replacement for lexical search.&lt;/p&gt;

&lt;p&gt;Users search for exact place names, train lines, product codes, legal terms, error messages, and quoted phrases. A model may produce similar embeddings for related text, but an exact identifier often deserves a lexical match.&lt;/p&gt;

&lt;p&gt;OpenSearch can combine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;full-text queries;&lt;/li&gt;
&lt;li&gt;BM25-style lexical relevance;&lt;/li&gt;
&lt;li&gt;field filters;&lt;/li&gt;
&lt;li&gt;aggregations;&lt;/li&gt;
&lt;li&gt;geographic queries;&lt;/li&gt;
&lt;li&gt;vector search;&lt;/li&gt;
&lt;li&gt;search-oriented dashboards and operational tooling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the travel assistant, it could answer questions 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;Find pages containing "Kiyomizu-dera" in English,
published in the Kyoto corpus, with an opening-hours field updated this month.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That query contains exact text, metadata, and freshness requirements. Those signals should not be reduced to one vector distance.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.opensearch.org/latest/about/" rel="noopener noreferrer"&gt;OpenSearch documentation&lt;/a&gt;&lt;br&gt;
covers both lexical and vector-oriented search, making it a possible single-engine choice when those retrieval modes need to work together.&lt;/p&gt;
&lt;h2&gt;
  
  
  Qdrant and vector databases: semantic similarity
&lt;/h2&gt;

&lt;p&gt;Vector databases are designed around a different question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which stored items are closest to this query in embedding space?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is valuable when the user and document do not share the same words.&lt;/p&gt;

&lt;p&gt;For example, a visitor might ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Where can I spend a quiet indoor afternoon learning about local history?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A useful museum description may not contain that exact sentence. Semantic retrieval can still bring it into the candidate set.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://qdrant.tech/documentation/overview/" rel="noopener noreferrer"&gt;Qdrant&lt;/a&gt; stores vectors with payload metadata and supports filtered similarity search. A typical request first restricts the eligible points, then ranks the remaining points by vector distance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;metadata filter
  -&amp;gt; approximate nearest-neighbor search
  -&amp;gt; top candidates
  -&amp;gt; optional reranker
  -&amp;gt; LLM context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an effective pattern, but it leaves an architectural question: how does the application decide the right filter and search namespace before similarity ranking begins?&lt;/p&gt;

&lt;p&gt;Vector database similarity search algorithms, index type, filtering behavior, memory use, and update patterns all affect the result. A RAG vector DB comparison among Pinecone, Qdrant, pgvector, or OpenSearch should use the application's own corpus and filters. Lists of recommended vector databases cannot replace workload-specific evaluation.&lt;/p&gt;

&lt;h2&gt;
  
  
  RAG hybrid search: BM25, vectors, and reranking
&lt;/h2&gt;

&lt;p&gt;Keyword and semantic retrieval are complementary. A RAG hybrid search pipeline can combine BM25 and vector scores so that exact names and identifiers remain visible while semantically related documents can still be discovered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;metadata and permission filters
  -&amp;gt; BM25 lexical candidates
  + vector similarity candidates
  -&amp;gt; merge and deduplicate
  -&amp;gt; reranker
  -&amp;gt; token-bounded context
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For RAG accuracy improvement, rerank integration is often more useful than simply increasing the first-stage result count. A reranker can inspect a small candidate set more carefully, while the first stage remains optimized for recall.&lt;/p&gt;

&lt;p&gt;RAG chunk size optimization techniques matter too. Small chunks can improve matching precision but lose surrounding meaning; large chunks preserve context but consume more tokens and may dilute relevance. The right chunk size depends on document structure, retrieval method, and the unit that must be cited.&lt;/p&gt;

&lt;p&gt;RAG frameworks such as LangChain and LlamaIndex can connect loaders, embeddings, retrievers, and models. A LangChain RAG setup sample can demonstrate the wiring, but a framework cannot decide the correct authorization boundary, freshness rule, or candidate scope for an application.&lt;/p&gt;

&lt;p&gt;An open-source RAG setup for internal search can combine PostgreSQL, OpenSearch or Qdrant, a local model, and an orchestration framework. Self-hosting changes the operations and privacy boundary, but it does not remove the need for the same retrieval and evaluation decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  RAG accuracy improvement and evaluation metrics
&lt;/h2&gt;

&lt;p&gt;Retrieval quality and answer quality should be measured separately. Useful RAG evaluation metrics include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;recall and precision at &lt;code&gt;k&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;mean reciprocal rank or nDCG;&lt;/li&gt;
&lt;li&gt;context relevance and context precision;&lt;/li&gt;
&lt;li&gt;groundedness or faithfulness;&lt;/li&gt;
&lt;li&gt;citation correctness;&lt;/li&gt;
&lt;li&gt;answer completeness;&lt;/li&gt;
&lt;li&gt;retrieved tokens, latency, and cost per request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tools such as Ragas can help automate parts of evaluation, but no RAG evaluation tool or tutorial can define the correct expected answer for every business workflow. A small, reviewed test set from real questions is still valuable.&lt;/p&gt;

&lt;p&gt;RAG hallucination prevention measures should include more than adding extra documents. Stronger measures include current sources, explicit citations, permission checks, conflict detection, an option to abstain, and tests for questions whose answers are not present in the knowledge base.&lt;/p&gt;

&lt;p&gt;RAG knowledge base automated updates also need versioning and invalidation. A pipeline should know which source revision produced a chunk, when an embedding must be rebuilt, and how deleted or expired information leaves caches and indexes.&lt;/p&gt;

&lt;h2&gt;
  
  
  RAG security measures and implementation cost
&lt;/h2&gt;

&lt;p&gt;RAG security measures begin before retrieval. Tenant isolation, source authorization, secret removal, encryption, audit trails, and defenses against prompt injection in retrieved documents all belong in the design. General database security measures still apply even when the database is described as an AI component.&lt;/p&gt;

&lt;p&gt;Organizations may also need to align collection and use of data with privacy rules, sector obligations, AI legal regulations, and their own AI ethics guidelines. Retrieval makes data available to a model; it does not create permission to use that data.&lt;/p&gt;

&lt;p&gt;A complete RAG cost comparison includes ingestion, embedding generation, database storage, cache, network transfer, reranking, LLM input tokens, monitoring, backup, and engineering time. For a small business, the average AI implementation cost cannot be inferred from the model API price alone.&lt;/p&gt;

&lt;p&gt;RAG cost reduction and token savings are closely related to candidate scope.&lt;/p&gt;

&lt;p&gt;Passing fewer irrelevant records through retrieval and reranking can reduce database work and LLM input together. For local deployments, local LLM setup and PC requirements are a separate capacity decision, but smaller retrieved contexts can still reduce downstream memory and compute pressure.&lt;/p&gt;

&lt;p&gt;Many AI implementation failures happen because a demo is evaluated only on a few successful questions. The practical AI implementation benefits appear when the system also handles stale data, missing answers, access boundaries, cost limits, and failure recovery.&lt;/p&gt;

&lt;h2&gt;
  
  
  The difficult part is often candidate scope
&lt;/h2&gt;

&lt;p&gt;Consider this question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I am at Kiyomizu-dera. It is raining, and I have two hours. What should I do&lt;br&gt;
next?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Several retrieval methods can help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;lexical search can find pages that mention Kiyomizu-dera;&lt;/li&gt;
&lt;li&gt;geographic search can find coordinates within a radius;&lt;/li&gt;
&lt;li&gt;vector search can find semantically similar travel advice;&lt;/li&gt;
&lt;li&gt;SQL can enforce opening hours, permissions, and reservation constraints;&lt;/li&gt;
&lt;li&gt;a reranker can reorder the resulting candidates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The challenge is not that any one of these methods is bad. The challenge is deciding what should enter the candidate set at all.&lt;/p&gt;

&lt;p&gt;A useful answer may need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;nearby landmarks;&lt;/li&gt;
&lt;li&gt;an indoor museum or gallery;&lt;/li&gt;
&lt;li&gt;a restaurant that is currently open;&lt;/li&gt;
&lt;li&gt;a temporary event happening today;&lt;/li&gt;
&lt;li&gt;transit information;&lt;/li&gt;
&lt;li&gt;accessibility information;&lt;/li&gt;
&lt;li&gt;places already present in the user's itinerary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The application knows the center: the visitor, Kiyomizu-dera, the current day, and the current situation. It does not yet know the exact boundary of the useful answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Semantic similarity is not operational relevance
&lt;/h2&gt;

&lt;p&gt;The most semantically similar document is not necessarily the most useful document for the current request.&lt;/p&gt;

&lt;p&gt;A beautifully written article about another Japanese temple may be close in embedding space but unusable within two hours. A short transit notice may be semantically distant from a tourism question but essential to the answer. A restaurant record may contain almost none of the words in the user's request, yet be relevant because it is nearby, open, and compatible with the itinerary.&lt;/p&gt;

&lt;p&gt;RAG systems commonly address this with metadata filters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Kyoto"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"area"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Higashiyama"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"openNow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"weather"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"categories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"restaurant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"museum"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"landmark"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is reasonable. But as the product grows, the filter becomes an&lt;br&gt;
application-level retrieval plan. Someone must maintain the rules, namespaces, joins, and fan-out reads that reconstruct the context for every request.&lt;/p&gt;
&lt;h2&gt;
  
  
  Geographic distance is not the complete boundary either
&lt;/h2&gt;

&lt;p&gt;If the question is simply "what is within one kilometer?", a geographic index such as PostGIS or OpenSearch geo search is the direct solution.&lt;/p&gt;

&lt;p&gt;Useful travel context is not always a circle, however.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A river, hill, or railway can make two close points inconvenient to reach.&lt;/li&gt;
&lt;li&gt;Rain changes which facilities are useful.&lt;/li&gt;
&lt;li&gt;A family and a solo traveler may need different neighborhoods.&lt;/li&gt;
&lt;li&gt;Opening hours and event dates change the available context.&lt;/li&gt;
&lt;li&gt;Editorial groupings such as "rainy-day Kyoto" are not geographic regions.&lt;/li&gt;
&lt;li&gt;One place can belong to an area guide, a station guide, and a personal
itinerary at the same time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Distance remains an important signal. It is simply not the only relationship that defines the field of view.&lt;/p&gt;
&lt;h2&gt;
  
  
  The application keeps rebuilding context
&lt;/h2&gt;

&lt;p&gt;A conventional stack can solve this problem. It may use SQL joins, geographic indexes, search filters, a graph, application-maintained ID lists, and vector ranking.&lt;/p&gt;

&lt;p&gt;The repeated work is reconstructing the same locality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;start from the current place
  -&amp;gt; identify the area
  -&amp;gt; find allowed categories
  -&amp;gt; join today's events
  -&amp;gt; add relevant transit
  -&amp;gt; add itinerary items
  -&amp;gt; build candidates
  -&amp;gt; rank candidates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The missing operation here is not another scoring algorithm. It is a reusable way to answer this earlier question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What belongs in the neighborhood of this known center for this kind of&lt;br&gt;
request?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Treat the reusable neighborhood as data
&lt;/h2&gt;

&lt;p&gt;One design option is to store that neighborhood instead of reconstructing it from scratch for every request.&lt;/p&gt;

&lt;p&gt;This does not automatically require a new database. An application can model the relationship with SQL junction tables, graph edges, materialized views, search documents, or maintained ID lists. The important change is conceptual:&lt;br&gt;
candidate scope becomes data with its own lifecycle, rather than temporary query logic assembled immediately before ranking.&lt;/p&gt;

&lt;p&gt;For the tourism example, a reusable neighborhood needs a few properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;each place should keep one canonical record;&lt;/li&gt;
&lt;li&gt;the same place should be able to appear in several contexts;&lt;/li&gt;
&lt;li&gt;adding a place to a guide should not copy its payload;&lt;/li&gt;
&lt;li&gt;reads should be bounded by category, depth, filters, limits, and token cost;&lt;/li&gt;
&lt;li&gt;lexical or vector ranking should remain available after the neighborhood is
selected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this relationship is occasional, application code may be enough. If the same pattern appears across users, places, projects, products, or time windows, it can be useful to make locality a first-class retrieval primitive.&lt;/p&gt;
&lt;h2&gt;
  
  
  How KoutenDB represents this retrieval pattern
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/puffball1567/koutendb" rel="noopener noreferrer"&gt;KoutenDB&lt;/a&gt; is one database designed&lt;br&gt;
around that pattern. It uses coordinate-like &lt;code&gt;ring&lt;/code&gt; placement for canonical records and &lt;code&gt;stellar&lt;/code&gt; visibility lenses for reusable views across those coordinates.&lt;/p&gt;

&lt;p&gt;An importer or application can keep tourism data in canonical rings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;landmarks/kyoto/kiyomizudera
landmarks/kyoto/yasaka-pagoda
restaurants/kyoto/gion
culture/kyoto/national-museum
events/kyoto/2026-07-23
transit/kyoto/higashiyama
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can then attach useful coordinates to an area-centered view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kouten stellar attach &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;travel/kyoto/higashiyama &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;landmarks/kyoto/kiyomizudera

kouten stellar attach &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;travel/kyoto/higashiyama &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;restaurants/kyoto/gion

kouten stellar attach &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;travel/kyoto/higashiyama &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;culture/kyoto/national-museum

kouten stellar attach &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;travel/kyoto/higashiyama &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;events/kyoto/2026-07-23
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application can read the surrounding context from the known center:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kouten get &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;travel/kyoto/higashiyama
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or narrow the same field of view to one category:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kouten get &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;travel/kyoto/higashiyama &lt;span class="nt"&gt;--subring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;restaurants
kouten get &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;travel/kyoto/higashiyama &lt;span class="nt"&gt;--subring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;culture
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same restaurant can also be visible from a rainy-day guide, a&lt;br&gt;
station-centered guide, or a user's itinerary without copying its canonical payload.&lt;/p&gt;

&lt;p&gt;KoutenDB does not infer geographic or editorial relevance from a path name.&lt;/p&gt;

&lt;p&gt;The application, importer, or curation process still decides which coordinates belong in each neighborhood. The database preserves that decision so later reads do not have to reconstruct it from scratch.&lt;/p&gt;
&lt;h2&gt;
  
  
  This is more than a path prefix
&lt;/h2&gt;

&lt;p&gt;A ring name deliberately looks like a readable path. Hierarchy is useful when parent and child data naturally belong together.&lt;/p&gt;

&lt;p&gt;A path prefix alone, however, represents one tree. Real records participate in several views at once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;canonical catalog: restaurants/kyoto/gion/example-cafe
area guide:        travel/kyoto/higashiyama
weather guide:     travel/kyoto/rainy-day
personal plan:     travel/users/123/today
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copying the restaurant under every path creates synchronization work. Moving it to one path weakens the other views. A stellar lens instead stores a visibility relationship across existing coordinates.&lt;/p&gt;

&lt;p&gt;The caller supplies a center and a cost boundary. Depth, branch budget, subrings, filters, projections, sorting, and limits control how much context is returned. The caller does not have to enumerate every individual record in the answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Locality can precede vector ranking
&lt;/h2&gt;

&lt;p&gt;Locality-aware retrieval and vector search are not mutually exclusive.&lt;/p&gt;

&lt;p&gt;A combined pipeline can look 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;known center: user + place + time
  -&amp;gt; retrieve the configured local neighborhood
  -&amp;gt; apply current constraints
  -&amp;gt; vector-rank the smaller candidate set if needed
  -&amp;gt; rerank and enforce a token budget
  -&amp;gt; LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The vector database asks which candidates are semantically close. The locality layer asks which part of the data should be considered first.&lt;/p&gt;

&lt;p&gt;This can reduce the number of payloads loaded, vectors compared, records reranked, bytes transferred, and tokens considered downstream. It does not guarantee that every query becomes faster. The model is useful only when the application can express meaningful locality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Match the database to the retrieval question
&lt;/h2&gt;

&lt;p&gt;The database decision becomes clearer when it starts from the question rather than the current RAG trend.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Retrieval question&lt;/th&gt;
&lt;th&gt;Natural starting point&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Which exact record or relationship is valid?&lt;/td&gt;
&lt;td&gt;Relational query and indexes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Which JSON documents match known fields?&lt;/td&gt;
&lt;td&gt;Document database or SQL/JSON&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Which result can be reused briefly?&lt;/td&gt;
&lt;td&gt;Cache or session store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Which documents contain these words?&lt;/td&gt;
&lt;td&gt;Full-text search engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Which documents are semantically similar?&lt;/td&gt;
&lt;td&gt;Vector search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Which places are inside a literal radius?&lt;/td&gt;
&lt;td&gt;Geographic index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Which context is useful around this known user, place, project, or time?&lt;/td&gt;
&lt;td&gt;Locality-aware neighborhood retrieval&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are not exclusive choices. PostgreSQL may remain the source of truth while OpenSearch handles lexical retrieval. A vector database may rank semantically similar chunks. KoutenDB may be used as a locality-aware document and retrieval store, or as an earlier candidate-selection stage.&lt;/p&gt;

&lt;p&gt;A smaller system may choose one database that covers enough of these roles. A larger system may separate them. Additional infrastructure is justified only when it removes a measured retrieval, correctness, or operations problem.&lt;/p&gt;

&lt;p&gt;Practical database design steps for RAG are therefore:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;identify the authoritative source and update owner;&lt;/li&gt;
&lt;li&gt;define tenant, user, and document access boundaries;&lt;/li&gt;
&lt;li&gt;list exact, lexical, semantic, geographic, and locality-aware queries;&lt;/li&gt;
&lt;li&gt;choose the smallest set of database types that covers those queries;&lt;/li&gt;
&lt;li&gt;measure retrieval quality, tokens, latency, and failure cases;&lt;/li&gt;
&lt;li&gt;document database backup methods and database migration steps for the data that cannot be rebuilt;&lt;/li&gt;
&lt;li&gt;use database performance tuning only after the candidate and access model are correct.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The final question is what the application already knows
&lt;/h2&gt;

&lt;p&gt;RAG architecture often begins by asking which embedding model or vector database to use.&lt;/p&gt;

&lt;p&gt;Another useful question comes first:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What does the application already know before retrieval begins?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It may already know the authenticated user, tenant, order, project, document group, current place, time window, or active task. That information can define a much smaller and more relevant starting region than the complete corpus.&lt;/p&gt;

&lt;p&gt;If the request begins with no meaningful center, global lexical or vector search may be exactly right. If the request begins with a known center but an uncertain context boundary, preserving locality can keep the retrieval problem smaller before ranking and prompt construction begin.&lt;/p&gt;

&lt;p&gt;The important choice is not SQL versus vectors, or one database product versus another. It is deciding whether the application needs exact truth, lexical matching, semantic similarity, geographic proximity, or a reusable neighborhood of context—and then giving each retrieval stage the data model it actually needs.&lt;/p&gt;

&lt;p&gt;That is the foundation of a RAG database design that can grow beyond a demo.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>rag</category>
      <category>llm</category>
    </item>
    <item>
      <title>Why KoutenDB Exists: You Know the Center, Not the Boundary</title>
      <dc:creator>puffball1567</dc:creator>
      <pubDate>Wed, 22 Jul 2026 08:04:06 +0000</pubDate>
      <link>https://dev.to/puffball1567/why-koutendb-exists-you-know-the-center-not-the-boundary-2fga</link>
      <guid>https://dev.to/puffball1567/why-koutendb-exists-you-know-the-center-not-the-boundary-2fga</guid>
      <description>&lt;p&gt;Most database queries begin by describing the answer.&lt;/p&gt;

&lt;p&gt;You provide a primary key, write a SQL predicate, select a collection, enter&lt;br&gt;
search terms, or send an embedding to a vector database. The database then&lt;br&gt;
finds the records that match.&lt;/p&gt;

&lt;p&gt;That model works extremely well, but it is not the shape of every application&lt;br&gt;
request.&lt;/p&gt;

&lt;p&gt;Sometimes the application knows &lt;strong&gt;where the answer should begin&lt;/strong&gt;, while the&lt;br&gt;
complete boundary of useful information is still unclear.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a support request starts with a customer, but may need their profile, recent orders, the product involved, previous tickets, and a refund policy.&lt;/li&gt;
&lt;li&gt;an incident investigation starts with a service and a point in time, but may need a nearby deployment, error logs, metrics, and related service events.&lt;/li&gt;
&lt;li&gt;a RAG request starts with a project or topic, but the useful context may span documents, summaries, source metadata, and attached knowledge.&lt;/li&gt;
&lt;li&gt;a game request starts with a player or place, but may need nearby entities, inventory, quests, and regional state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In each case, the center is known. The exact context boundary is not.&lt;/p&gt;

&lt;p&gt;That is the problem KoutenDB was created to explore.&lt;/p&gt;
&lt;h2&gt;
  
  
  Exact lookup is not the same as context retrieval
&lt;/h2&gt;

&lt;p&gt;Suppose an API receives an order ID.&lt;/p&gt;

&lt;p&gt;Finding the order itself is easy. Almost any database can do that efficiently.&lt;br&gt;
The harder question is what else should travel with it.&lt;/p&gt;

&lt;p&gt;Should the response include the customer? The shop? Recent payment events? The specific product documentation? An unresolved support ticket? The answer may depend on the request, product, current state, and amount of context the caller can afford to process.&lt;/p&gt;

&lt;p&gt;A conventional application can assemble that context with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SQL joins;&lt;/li&gt;
&lt;li&gt;several document lookups;&lt;/li&gt;
&lt;li&gt;application-maintained ID lists;&lt;/li&gt;
&lt;li&gt;a graph traversal;&lt;/li&gt;
&lt;li&gt;metadata filters;&lt;/li&gt;
&lt;li&gt;full-text or vector search;&lt;/li&gt;
&lt;li&gt;a cache built for one response shape.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are all valid solutions. But they usually require the application to reconstruct the neighborhood every time it reads.&lt;/p&gt;

&lt;p&gt;KoutenDB asks a different question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What if the database preserved useful locality when the data was written, so&lt;br&gt;
a later read could begin from that locality directly?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  A ring is a coordinate, not just a container
&lt;/h2&gt;

&lt;p&gt;KoutenDB stores a record with a coordinate-like &lt;code&gt;ring&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;users/123
users/123/orders
users/123/support
shops/1123
orders/A-001
products/sku-9/docs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The names are readable, but readability is not their main purpose. A ring is part of data placement and part of the read path.&lt;/p&gt;

&lt;p&gt;If the application starts from &lt;code&gt;users/123&lt;/code&gt;, KoutenDB can inspect that local hierarchy without beginning with an unrelated global collection. If it starts from &lt;code&gt;orders/A-001&lt;/code&gt;, it can use an order-centered view instead.&lt;/p&gt;

&lt;p&gt;This turns locality known by the application into retrieval information the database can retain.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same data can be visible from another center
&lt;/h2&gt;

&lt;p&gt;Related context does not always fit one permanent hierarchy. An order belongs to a customer, a shop, and a product at the same time.&lt;/p&gt;

&lt;p&gt;KoutenDB therefore has a &lt;code&gt;stellar&lt;/code&gt; lens. It can attach existing rings to a useful center without copying their payloads.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kouten put &lt;span class="nt"&gt;--ring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;users&lt;/span&gt;/123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{"kind":"user","name":"Alice"}'&lt;/span&gt; &lt;span class="nt"&gt;--codec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;json

kouten put &lt;span class="nt"&gt;--ring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;shops/1123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{"kind":"shop","name":"Orbit Store"}'&lt;/span&gt; &lt;span class="nt"&gt;--codec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;json

kouten put &lt;span class="nt"&gt;--ring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;orders/A-001 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{"kind":"order","orderNo":"A-001","total":42}'&lt;/span&gt; &lt;span class="nt"&gt;--codec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;json

kouten stellar attach &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;commerce/order/A-001 &lt;span class="nt"&gt;--ring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;users&lt;/span&gt;/123

kouten stellar attach &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;commerce/order/A-001 &lt;span class="nt"&gt;--ring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;shops/1123

kouten stellar attach &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;commerce/order/A-001 &lt;span class="nt"&gt;--ring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;orders/A-001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the application can read from the order as the center:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kouten get &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;commerce/order/A-001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The original records remain in their original rings. Attaching or detaching a ring changes what the lens can see; it does not duplicate or delete the payload.&lt;/p&gt;

&lt;p&gt;This is useful when the same information needs to participate in several context views without turning each view into another copied document.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: retrieving useful information around a tourist destination
&lt;/h2&gt;

&lt;p&gt;Travel information makes the center-versus-boundary problem easier to see.&lt;/p&gt;

&lt;p&gt;Imagine that a visitor is looking at Kiyomizu-dera in Kyoto. The application knows the starting point, but a useful response probably should not contain only the temple record.&lt;/p&gt;

&lt;p&gt;The visitor may also need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;other nearby landmarks;&lt;/li&gt;
&lt;li&gt;restaurants and cafes;&lt;/li&gt;
&lt;li&gt;museums, galleries, and cultural facilities;&lt;/li&gt;
&lt;li&gt;events taking place that day;&lt;/li&gt;
&lt;li&gt;transit information;&lt;/li&gt;
&lt;li&gt;accessibility or opening-hour information.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no single permanent boundary for that context. A family, a solo traveler, and someone planning a rainy afternoon may need different parts of the same surrounding information.&lt;/p&gt;

&lt;p&gt;An importer or application can keep each place in a canonical ring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;landmarks/kyoto/kiyomizudera
landmarks/kyoto/yasaka-pagoda
restaurants/kyoto/gion
culture/kyoto/national-museum
events/kyoto/2026-07-22
transit/kyoto/higashiyama
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It can then attach the useful coordinates to an area-centered stellar lens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kouten stellar attach &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;travel/kyoto/higashiyama &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;landmarks/kyoto/kiyomizudera

kouten stellar attach &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;travel/kyoto/higashiyama &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;landmarks/kyoto/yasaka-pagoda

kouten stellar attach &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;travel/kyoto/higashiyama &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;restaurants/kyoto/gion

kouten stellar attach &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;travel/kyoto/higashiyama &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;culture/kyoto/national-museum

kouten stellar attach &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;travel/kyoto/higashiyama &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--ring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;events/kyoto/2026-07-22
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application can now begin with the Higashiyama area and retrieve the attached tourism context in one read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kouten get &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;travel/kyoto/higashiyama
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or it can narrow the same view to restaurants or cultural facilities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kouten get &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;travel/kyoto/higashiyama &lt;span class="nt"&gt;--subring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;restaurants
kouten get &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;travel/kyoto/higashiyama &lt;span class="nt"&gt;--subring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;culture
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same restaurant can also be attached to a &lt;code&gt;travel/kyoto/rainy-day&lt;/code&gt; lens, a station-centered lens, or a day-plan lens without moving or copying its canonical payload.&lt;/p&gt;

&lt;p&gt;KoutenDB does not automatically infer geographic relevance from these names.&lt;br&gt;
The application, importer, or curation process still decides which coordinates belong in the neighborhood. The database's role is to preserve that decision and make it directly readable later.&lt;/p&gt;

&lt;p&gt;This is the intended meaning of surrounding information: the caller starts from a useful place, receives several categories of nearby context together, and can narrow the field of view without having to list every individual place in the request.&lt;/p&gt;
&lt;h2&gt;
  
  
  Is this just a path?
&lt;/h2&gt;

&lt;p&gt;A ring such as &lt;code&gt;landmarks/kyoto/kiyomizudera&lt;/code&gt; certainly looks like an ordinary path. KoutenDB deliberately uses readable hierarchical names because paths are a useful way to express parent and child locality.&lt;/p&gt;

&lt;p&gt;But a path by itself provides only hierarchy.&lt;/p&gt;

&lt;p&gt;If all tourism data lived under one tree, an application could read a prefix&lt;br&gt;
such as &lt;code&gt;travel/kyoto/higashiyama/*&lt;/code&gt;. That works when every useful relationship fits the same permanent hierarchy and the caller already knows which branches to request.&lt;/p&gt;

&lt;p&gt;The difficulty is that real data belongs to several contexts at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a restaurant belongs to its canonical restaurant catalog;&lt;/li&gt;
&lt;li&gt;geographically, it belongs to an area;&lt;/li&gt;
&lt;li&gt;operationally, it may belong to a currently open list;&lt;/li&gt;
&lt;li&gt;editorially, it may belong to a rainy-day guide;&lt;/li&gt;
&lt;li&gt;personally, it may belong to one visitor's itinerary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Duplicating the same record under every path creates synchronization and consistency work. Moving it into one path makes the other views harder to express. Storing only path strings leaves the application responsible for maintaining and joining all of those relationships.&lt;/p&gt;

&lt;p&gt;KoutenDB adds two things beyond the path syntax:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the ring hierarchy is understood by the retrieval engine and can be traversed with explicit depth, branch, filter, limit, sort, and projection controls.&lt;/li&gt;
&lt;li&gt;stellar metadata can connect rings from different hierarchies into another coordinate-centered view without changing their canonical paths or copying their payloads.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The stellar lens is therefore not another directory. It is a stored visibility relationship across existing coordinates.&lt;/p&gt;

&lt;p&gt;The same ring coordinate can also act as a boundary for authorization, dump/import, placement, and synchronization. A plain path string has none of those database semantics unless the application builds them separately.&lt;/p&gt;

&lt;p&gt;So the path is the readable address. The KoutenDB-specific part is how that address participates in placement, neighborhood retrieval, and reusable views across otherwise separate branches.&lt;/p&gt;
&lt;h2&gt;
  
  
  Not knowing the boundary does not mean an unbounded query
&lt;/h2&gt;

&lt;p&gt;"The caller does not know the exact boundary" should not mean "read&lt;br&gt;
everything."&lt;/p&gt;

&lt;p&gt;The caller still controls the field of view. KoutenDB can narrow a read with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a subring;&lt;/li&gt;
&lt;li&gt;hierarchy depth;&lt;/li&gt;
&lt;li&gt;branch budget;&lt;/li&gt;
&lt;li&gt;per-ring limits and sorting;&lt;/li&gt;
&lt;li&gt;filters;&lt;/li&gt;
&lt;li&gt;field selection;&lt;/li&gt;
&lt;li&gt;an overall result limit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, the same order-centered view can request only shop data or only a small projection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kouten get &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;commerce/order/A-001 &lt;span class="nt"&gt;--subring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;shops

kouten get &lt;span class="nt"&gt;--stellar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;commerce/order/A-001 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--selection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{ kind name orderNo total }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The distinction is important:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The application supplies a meaningful center and a cost boundary. It does&lt;br&gt;
not have to enumerate every record relationship that may form the answer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is why KoutenDB is intended for surrounding information rather than only exactly bounded queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not use a collection or partition key?
&lt;/h2&gt;

&lt;p&gt;Collections, namespaces, partitions, and shards can already reduce the amount of data a database examines.&lt;/p&gt;

&lt;p&gt;The difference is what they represent.&lt;/p&gt;

&lt;p&gt;A partition key commonly answers questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where is this record stored?&lt;/li&gt;
&lt;li&gt;Which node owns it?&lt;/li&gt;
&lt;li&gt;Which partition can be pruned?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;KoutenDB also wants locality to answer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What information is likely to be useful around this record?&lt;/li&gt;
&lt;li&gt;Which nearby rings should enter the current field of view?&lt;/li&gt;
&lt;li&gt;Can the same existing ring become visible from another useful center?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That makes placement an application-visible retrieval concept, not only an internal scaling decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not begin with vector search?
&lt;/h2&gt;

&lt;p&gt;Vector search answers a valuable question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which items are semantically similar to this query?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But similarity is not the same as operational relevance.&lt;/p&gt;

&lt;p&gt;A customer's latest order, a permission record, and the applicable refund policy may be necessary context even when their embeddings are not the closest matches in a global corpus. Systems often compensate by adding namespaces and metadata filters before vector ranking.&lt;/p&gt;

&lt;p&gt;KoutenDB makes that first narrowing step the main data model:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;begin from a known user, order, tenant, topic, place, or time coordinate;&lt;/li&gt;
&lt;li&gt;open the useful local neighborhood;&lt;/li&gt;
&lt;li&gt;apply filters, vector ranking, reranking, or an LLM only to that smaller
working set when needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Vectors remain useful. They are optional rather than the only way to discover related context.&lt;/p&gt;

&lt;p&gt;For local AI and RAG systems, that distinction can matter. The expensive work is not limited to the final LLM call. Loading candidates, comparing vectors, reranking chunks, transferring payloads, and filling a context window all have a cost. A smaller first-stage working set can reduce work throughout the pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not keep this entirely in application code?
&lt;/h2&gt;

&lt;p&gt;An application can maintain arrays of related IDs, write join tables, perform fan-out reads, and build cached response documents. Many applications already do.&lt;/p&gt;

&lt;p&gt;The tradeoff is that locality becomes scattered across code and supporting infrastructure. One rule determines reads, another determines authorization, another controls exports, and another decides how data should be copied or synchronized.&lt;/p&gt;

&lt;p&gt;KoutenDB's larger design goal is to let the same coordinate participate in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;retrieval scope;&lt;/li&gt;
&lt;li&gt;authorization scope;&lt;/li&gt;
&lt;li&gt;dump and import boundaries;&lt;/li&gt;
&lt;li&gt;placement and compaction;&lt;/li&gt;
&lt;li&gt;delayed synchronization between topologies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The value is not just fewer lines in one query. It is keeping the application's concept of locality visible across the data lifecycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  The database cost should follow the working set
&lt;/h2&gt;

&lt;p&gt;Let the complete corpus contain &lt;code&gt;N&lt;/code&gt; records, while the useful neighborhood for one request contains &lt;code&gt;k&lt;/code&gt; records.&lt;/p&gt;

&lt;p&gt;Many systems eventually introduce a first-stage index or filter so later work does not operate over all &lt;code&gt;N&lt;/code&gt;. KoutenDB's central bet is that meaningful placement can provide part of that first stage directly.&lt;/p&gt;

&lt;p&gt;The goal is to make more of the request cost follow &lt;code&gt;k&lt;/code&gt;, the useful local working set, instead of repeatedly rediscovering locality from the full corpus.&lt;/p&gt;

&lt;p&gt;This is not an automatic performance guarantee. It depends on whether the application can express useful locality. If all queries are arbitrary and global, there may be no meaningful center to exploit.&lt;/p&gt;

&lt;p&gt;But many applications already begin with one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an authenticated user;&lt;/li&gt;
&lt;li&gt;a tenant;&lt;/li&gt;
&lt;li&gt;an order;&lt;/li&gt;
&lt;li&gt;a product;&lt;/li&gt;
&lt;li&gt;a project;&lt;/li&gt;
&lt;li&gt;a document group;&lt;/li&gt;
&lt;li&gt;a region;&lt;/li&gt;
&lt;li&gt;a service and time window;&lt;/li&gt;
&lt;li&gt;an entity in a simulation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;KoutenDB makes that starting knowledge part of the database query model.&lt;/p&gt;

&lt;h2&gt;
  
  
  When this model is useful
&lt;/h2&gt;

&lt;p&gt;KoutenDB is worth examining when all three of these statements are true:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;requests naturally begin from a known coordinate;&lt;/li&gt;
&lt;li&gt;the application frequently needs surrounding context, not only one exact
record;&lt;/li&gt;
&lt;li&gt;the useful context can be placed or attached meaningfully before it is
requested.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is less relevant when the main workload is arbitrary full-corpus analytics, global full-text search, independent lookup on every field, or strict relational constraints. Those problems already have databases built around them.&lt;/p&gt;

&lt;p&gt;The reason for another database is not that existing databases forgot how to look up records.&lt;/p&gt;

&lt;p&gt;It is that a growing class of application and AI requests does not begin with a complete description of the answer. It begins with a center.&lt;/p&gt;

&lt;p&gt;KoutenDB exists to make that center useful.&lt;/p&gt;

&lt;p&gt;The implementation, data-model demo, and reproducible benchmarks are available in the &lt;a href="https://github.com/puffball1567/koutendb" rel="noopener noreferrer"&gt;KoutenDB repository&lt;/a&gt;.&lt;/p&gt;

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