<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Nur Azhar's</title>
  <link href="https://blog.nurazhar.com/atom.xml" rel="self"/>
  <link href="https://blog.nurazhar.com"/>
  <updated>2026-06-13T23:01:47+00:00</updated>
  <id>https://blog.nurazhar.com</id>
  <author>
    <name>Nur Azhar</name>
  </author>
  <entry>
    <id>https://blog.nurazhar.com/ai-first-sdlc-and-cognitive-vertigo.html</id>
    <link href="https://blog.nurazhar.com/ai-first-sdlc-and-cognitive-vertigo.html"/>
    <title>AI-First SDLC: Designing for High Velocity and Cognitive Vertigo</title>
    <updated>2026-06-14T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><hr /><p>If you use autonomous coding agents for more than a few hours, you will hit a wall.</p><p>It is a silent, exhausting kind of fatigue. You didn’t write a single line of boilerplate, you didn&apos;t fight compiler configurations, and you didn&apos;t search documentation. Yet, your head is throbbing, you feel dizzy, and your mental stamina is completely shot.</p><p>This is <strong>Cognitive Vertigo (or Cognitive G-Force)</strong>. It is caused by the collapse of latency between <em>intent</em> and <em>execution</em>. When you are no longer limited by typing speed, your brain is forced into a continuous loop of high-frequency architecture evaluation, context-switching, and code review.</p><p>To survive a full-time role in this new era without burning out, you must discard the traditional developer workflow and adopt an <strong>AI-First Software Development Lifecycle (SDLC)</strong>.</p><hr /><h2 id="the-paradigm-shift:-think-slow,-build-fast">The Paradigm Shift: Think Slow, Build Fast</h2><p>In the traditional SDLC, developers spend 80% of their energy coding and 20% designing. When coding, your brain operates in a slow, sequential state.</p><p>In an AI-First SDLC, this relationship is inverted:</p><pre><code class="language-mermaid">gantt
    title Traditional vs. AI-First Developer Lifecycles
    dateFormat  X
    axisFormat %s
    
    section Traditional SDLC (Active Typing)
    Design (Slow)          :active, des1, 0, 20
    Coding &amp; Debugging (Grind)  :active, cod1, 20, 100
    
    section AI-First SDLC (Agent Orchestration)
    Spec &amp; Architecture (80% Brain)  :crit, des2, 0, 80
    Agent Execution &amp; Tests (20% Verify) :crit, cod2, 80, 100
</code></pre><p>By separating the <strong>Design Phase</strong> (Think Slow) from the <strong>Execution Phase</strong> (Build Fast), you shield your brain from the constant feedback loops that drain your mental battery.</p><hr /><h2 id="core-pillars-of-the-ai-first-sdlc">Core Pillars of the AI-First SDLC</h2><p>Drawing inspiration from structured frameworks like <strong><a href="https://github.com/SteveGJones/ai-first-sdlc-practices">SteveGJones/ai-first-sdlc-practices</a></strong>, here is the blueprint to structure your day-to-day workflow for maximum stamina:</p><pre><code class="language-mermaid">graph TD
    A[1. Think Slow: Design &amp; Spec] --&gt;|Define boundaries, schemas, &amp; tests| B[2. The Constitution: Rules of Play]
    B --&gt;|Hand over blueprint to Agent| C[3. Build Fast: Async Execution]
    C --&gt;|Zero-Technical-Debt Pipeline| D[4. Verify: Black-Box Validation]
    D --&gt;|If tests fail| C
    D --&gt;|If tests pass| E[5. Ship &amp; Cooldown]
</code></pre><h3 id="1.-the-constitution-(declarative-rules)">1. The Constitution (Declarative Rules)</h3><p>Do not explain your design philosophy to an agent in every chat. Define a permanent configuration file (like a <code>CONSTITUTION.md</code> or <code>.clauderc</code>) in your repository root. This constitution outlines:</p><ul><li>Strict coding style and directory hierarchies.</li><li>Error-handling patterns.</li><li>Mandatory unit test structures.</li><li>Zero-technical-debt boundaries.</li></ul><p>By encoding your architectural standards in a file, the agent reads them as a system constraint. You no longer need to waste energy correcting style errors.</p><h3 id="2.-pipeline-validation-(validate---syntax/--quick/--pre-push)">2. Pipeline Validation (<code>validate --syntax/--quick/--pre-push</code>)</h3><p>Stop reading code line-by-line to verify correctness. This is a primary driver of cognitive fatigue. Instead, implement a multi-stage validation script in your workspace:</p><ul><li><strong><code>validate --syntax</code></strong>: Evaluates basic file structures and imports immediately after generation.</li><li><strong><code>validate --quick</code></strong>: Runs fast unit tests and linter audits before commits are written.</li><li><strong><code>validate --pre-push</code></strong>: Executes the full integration test suite and scans for security anomalies.</li></ul><p>If the validation script passes, trust the implementation. Focus your energy on verifying the boundaries, not inspecting the lines.</p><h3 id="3.-asynchronous-execution-(the-pacing-rule)">3. Asynchronous Execution (The Pacing Rule)</h3><p>Pair-programming with an agent in real-time is a cognitive trap. Instead, delegate asynchronously:</p><ol start="1"><li><strong>Write the Specification:</strong> Explicitly define the goal, the affected files, and the expected unit test changes in a task file (<code>task.md</code> or <code>implementation_plan.md</code>).</li><li><strong>Launch the Agent:</strong> Tell the agent to execute the specification and output its results.</li><li><strong>Step Away:</strong> Physically leave your keyboard. Walk around, drink water, or look out a window. Let the agent compile, write, and run tests in its own sandbox.</li><li><strong>Evaluate the Artifacts:</strong> Return only when the agent has completed the task and updated its walkthrough document.</li></ol><hr /><h2 id="daily-habits-to-collapse-cognitive-fatigue-to-zero">Daily Habits to Collapse Cognitive Fatigue to Zero</h2><p>Bookmark this checklist and use it as your daily operating system:</p><table><thead><tr><th style="text-align:left;">Habit</th><th style="text-align:left;">How to Implement</th><th style="text-align:left;">Why it Saves Your Brain</th></tr></thead><tbody><tr><td style="text-align:left;"><strong>Zero-Cache Brain</strong></td><td style="text-align:left;">Write all active tasks in a physical notepad or <code>task.md</code>. Never hold stack frames in your head.</td><td style="text-align:left;">Frees up working memory.</td></tr><tr><td style="text-align:left;"><strong>Test-Driven Intent</strong></td><td style="text-align:left;">Write the tests or output schemas first. Let the agent code until the tests pass.</td><td style="text-align:left;">Eliminates the need for line-by-line review.</td></tr><tr><td style="text-align:left;"><strong>Paced Breaks</strong></td><td style="text-align:left;">Set a timer to step away from the IDE for 5 minutes after every major agent execution.</td><td style="text-align:left;">Prevents cognitive overheating.</td></tr><tr><td style="text-align:left;"><strong>Interface Design</strong></td><td style="text-align:left;">Spend your time writing HoneySQL configurations, Malli schemas, or API signatures.</td><td style="text-align:left;">Keeps your brain in the &quot;System Architect&quot; tier.</td></tr></tbody></table><hr /><p><em>The speed of development is no longer bound by syntax. It is bound by your mental endurance. Structure your workflow to protect your mind first.</em></p></div>]]></content>
  </entry>
  <entry>
    <id>https://blog.nurazhar.com/sovereign-clojure-bitcoin-integration.html</id>
    <link href="https://blog.nurazhar.com/sovereign-clojure-bitcoin-integration.html"/>
    <title>Clojure &amp; Bitcoin: Building a Sovereign Node &amp; Integration Sandbox</title>
    <updated>2026-06-14T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><hr /><p>Lisp (specifically Clojure) and Bitcoin are a natural match. Both prioritize immutable data structures, declarative transformations, and minimal state side-effects.</p><p>In this post, we&apos;ll walk through the architectural blueprint of <strong><a href="https://github.com/nurazhardotcom/bsv-clj">bsv-clj</a></strong>, a sandbox project showing how to query Bitcoin node RPCs, map spendable UTXOs from local SQLite databases, validate transaction payloads, and redact sensitive data at the edge.</p><hr /><h2 id="1.-node-rpc-client-plumbing">1. Node RPC Client Plumbing</h2><p>The first layer is talking to the Bitcoin node itself over JSON-RPC. Using <code>http-kit</code> and <code>cheshire</code> (JSON parser), we can call remote node methods natively:</p><pre><code class="language-clojure">(def rpc-config
  {:url &quot;http://127.0.0.1:8332&quot;
   :user &quot;rpcuser&quot;
   :password &quot;rpcpassword&quot;})

(defn rpc-call
  &quot;Executes a JSON-RPC method call on the Bitcoin node.&quot;
  ([method] (rpc-call method []))
  ([method params]
   (let [payload (json/generate-string
                  {:jsonrpc &quot;2.0&quot;
                   :id &quot;clj-rpc&quot;
                   :method method
                   :params params})
         options {:headers {&quot;Content-Type&quot; &quot;application/json&quot;}
                  :basic-auth [(:user rpc-config) (:password rpc-config)]
                  :body payload}
         response @(http/post (:url rpc-config) options)]
     (if (= 200 (:status response))
       (json/parse-string (:body response) true)
       {:error (str &quot;HTTP Error: &quot; (:status response))}))))
</code></pre><p>With this, running <code>(rpc-call &quot;getblockchaininfo&quot;)</code> returns a native Clojure map representing the blockchain status.</p><hr /><h2 id="2.-local-first-sandbox:-utxo-tracking-via-sqlite">2. Local-First Sandbox: UTXO Tracking via SQLite</h2><p>Instead of querying third-party APIs (which exposes your financial privacy), the sovereign way is to read directly from your local wallet database.</p><p>Using <code>next.jdbc</code> and <code>honey.sql</code>, we query spendable outputs (UTXOs) from a local wallet sandbox:</p><pre><code class="language-clojure">(defn query-wallet-utxos
  &quot;Queries local SQLite database to list spendable outputs.&quot;
  [db-path]
  (let [ds (jdbc/get-datasource {:dbtype &quot;sqlite&quot; :dbname db-path})
        query (sql/format {:select [:txid :outputIndex :amount :state]
                           :from [:outputs]
                           :where [:= :state &quot;SPENDABLE&quot;]})]
    (jdbc/execute! ds query)))
</code></pre><hr /><h2 id="3.-transaction-validation-with-malli">3. Transaction Validation with Malli</h2><p>To prevent malformed transaction request structures from reaching signing nodes, we define a declarative schema using <strong><a href="https://github.com/metosin/malli">Malli</a></strong>:</p><pre><code class="language-clojure">(def TransactionRequestSchema
  [:map
   [:appId :string]
   [:inputs [:vector [:map
                      [:txid [:re #&quot;^[a-fA-F0-9]{64}$&quot;]]
                      [:outputIndex :int]]]]
   [:outputs [:vector [:map
                        [:toAddress [:re #&quot;^[13m-n2-9][a-km-zA-HJ-NP-Z1-9]{26,34}$&quot;]]
                        [:satoshis [:pos-int]]]]]
   [:metadata [:map
               [:timestamp :int]
               [:data :string]]]])
</code></pre><p>This enforces strict hexagonal boundaries: matching valid transaction hex patterns, valid Bitcoin addresses, positive satoshi spends, and clean metadata.</p><hr /><h2 id="4.-privacy-sentry:-data-loss-prevention-(dlp)">4. Privacy Sentry: Data Loss Prevention (DLP)</h2><p>When attaching raw text metadata directly into transaction payloads or OP_RETURN scripts, leakages can happen. We inject a regex redaction layer to scrub sensitive Singapore NRIC profiles before they hit the blockchain:</p><pre><code class="language-clojure">(def sg-nric-regex #&quot;(?i)\b[SGTFAM]\d{7}[A-Z]\b&quot;)

(defn redact-payload-metadata
  &quot;Scans metadata text and redacts sensitive Singapore NRIC profiles.&quot;
  [text]
  (clojure.string/replace text sg-nric-regex &quot;[REDACTED_NRIC]&quot;))
</code></pre><hr /><h2 id="why-this-architecture-fits-the-bitcoin-ethos">Why this Architecture Fits the Bitcoin Ethos</h2><p>By putting the RPC clients, local SQLite DBs, and payload validation under a Clojure workflow, we achieve:</p><ul><li><strong>Determinism</strong>: Zero side-effects inside key validations.</li><li><strong>Hermetic Runs</strong>: No dependency on cloud providers to run rules.</li><li><strong>Privacy</strong>: Data stays local and is scrubbed before broadcast.</li></ul><p>You can check out the sandbox project source code on GitHub: <strong><a href="https://github.com/nurazhardotcom/bsv-clj">github.com/nurazhardotcom/bsv-clj</a></strong>.</p></div>]]></content>
  </entry>
  <entry>
    <id>https://blog.nurazhar.com/cognitive-g-force-agentic-velocity.html</id>
    <link href="https://blog.nurazhar.com/cognitive-g-force-agentic-velocity.html"/>
    <title>Cognitive G-Force: Collapsing Development Time with Agentic Compilers</title>
    <updated>2026-06-14T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><hr /><p>A few minutes ago, I experienced a strange kind of development vertigo.</p><p>I was sitting at my desk, directing an AI agent to clean up some repositories, rename local folders, re-render my blog, write three distinct technical articles, and push commits to GitHub. In exactly <strong>46 minutes</strong>, we completed 8 distinct, multi-system tasks.</p><p>I literally told the agent: <em>&quot;I&apos;m experiencing G-force and feel like vomiting. It&apos;s too fast.&quot;</em></p><p>When your development environment transforms from a text editor into a highly-leveraged orchestration sandbox, the speed limit is no longer how fast you can type—it is how fast your mind can context-switch.</p><hr /><h2 id="the-46-minute-flight-log">The 46-Minute Flight Log</h2><p>Here is the exact timeline of what was accomplished between <strong>03:26 AM</strong> and <strong>04:12 AM</strong> this morning:</p><ol start="1"><li><strong>03:26 AM - Domain Migration</strong>: Transitioned the live site configuration from <code>clojure.nurazhar.com</code> to <code>blog.nurazhar.com</code>, mapping custom CNAMEs and updating DNS configurations.</li><li><strong>03:34 AM - OS Architecture Synthesis</strong>: Wrote and published an architectural overview of <strong>openSUSE Autonomous OS</strong>, detailing Snapper and Btrfs rollback recovery.</li><li><strong>03:46 AM - Profile &amp; Core Config Updates</strong>: Cleaned up the main GitHub profile, updated Babashka Quickblog parameters, and published a post on <strong>Quickblog&apos;s Minimalist Architecture</strong> (comparing it to Satoshi&apos;s peer-to-peer design philosophy).</li><li><strong>03:53 AM - Refactoring Workspace Directories</strong>: Renamed local folders to resolve domain drift, and added client-side <strong>Mermaid.js rendering support</strong> to the site&apos;s layout templates.</li><li><strong>03:58 AM - Local Code Analysis</strong>: Scanned a local, unpushed Clojure repository (<code>bsv-clj</code>), analyzed its Malli schemas, SQLite wallet querying, and DLP regex layers, and published a technical integration post about it.</li><li><strong>04:05 AM - Remote Audit &amp; Fixes</strong>: Identified and repaired broken repository links across the newly compiled site.</li><li><strong>04:11 AM - Git Auth Resolution</strong>: Diagnosed a failing remote Git push caused by dummy environment tokens, bypassed it to push all changes securely to GitHub via system keyring credentials, and audited the directory for obsolete references.</li></ol><hr /><h2 id="the-cognitive-shift:-from-typist-to-director">The Cognitive Shift: From Typist to Director</h2><p>Before agentic IDE tools, this workload would have taken <strong>half a day to a weekend</strong> of focused manual effort.</p><p>The time wouldn&apos;t be spent thinking; it would be consumed by the <strong>friction of execution</strong>:</p><ul><li>Swapping windows to check file paths.</li><li>Writing out boilerplate configurations.</li><li>Drafting paragraphs, checking spelling, and formatting code blocks.</li><li>Dealing with credentials, terminal syntax errors, and compiler bugs.</li></ul><p>When an agent handles the execution, you become the Director. You are no longer writing the code; you are <strong>compiling intent</strong>.</p><p>But this speed comes with a cost: <strong>Cognitive G-force</strong>. When you do not have to wait for compilers to run, files to save, or documentation to write, the latency between <em>designing a system</em> and <em>seeing it live</em> collapses to zero. You are constantly in high-throughput decision-making mode. It is exhilarating, but it can induce a literal sense of motion sickness.</p><p>The future of software engineering is no longer about learning syntax. It is about building the mental stamina to guide autonomous agents through complex architectures without losing your bearings.</p><hr /><p><em>How are you managing the cognitive load as developer velocity accelerates? Let&apos;s discuss.</em></p></div>]]></content>
  </entry>
  <entry>
    <id>https://blog.nurazhar.com/demystifying-arch-packaging-pkgbuilds-aur-cachyos.html</id>
    <link href="https://blog.nurazhar.com/demystifying-arch-packaging-pkgbuilds-aur-cachyos.html"/>
    <title>Demystifying Arch Linux Packaging: PKGBUILDs, the AUR, and CachyOS</title>
    <updated>2026-06-14T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><hr /><p>Arch Linux and its performance-optimized sibling, CachyOS, have one of the most powerful and elegant packaging systems in the entire Linux ecosystem. However, for newcomers and developers transitioning from other distributions, concepts like <strong>PKGBUILDs</strong>, the <strong>AUR</strong>, <strong>AUR helpers</strong> (like <code>paru</code>/<code>yay</code>), and <strong>custom repository forks</strong> can quickly become confusing.</p><p>If you have ever wondered:</p><ul><li><em>Is the AUR just full of binary files?</em></li><li><em>Why does CachyOS maintain its own custom PKGBUILD repository instead of using the AUR?</em></li><li><em>What is the actual step-by-step lifecycle of code turning into an installed package?</em></li></ul><p>This guide breaks it down with clear diagrams, comparisons, and technical details.</p><hr /><h2 id="the-cooking-analogy">🍳 The Cooking Analogy</h2><p>To understand this system, let&apos;s look at a quick analogy:</p><ul><li><strong>Source Code</strong> is the <strong>raw ingredients</strong> (vegetables, meat, spices) sitting on a farm (GitHub/GitLab).</li><li><strong>A PKGBUILD</strong> is the <strong>recipe</strong>. It tells you exactly how to prepare the ingredients, what pots to use, and how long to cook them.</li><li><strong>The AUR (Arch User Repository)</strong> is a <strong>public recipe-sharing website</strong>. Anyone can upload a recipe here.</li><li><strong>The Built Package</strong> is the <strong>cooked meal</strong> (e.g., a <code>.pkg.tar.zst</code> file). This is what you actually consume (install).</li><li><strong>An AUR Helper (like <code>paru</code>)</strong> is your <strong>automated personal chef</strong>. It visits the recipe site, fetches the instructions, downloads the ingredients, cooks it, and serves it to you.</li></ul><hr /><h2 id="the-arch-package-lifecycle">🗺️ The Arch Package Lifecycle</h2><p>Here is how source code on the internet eventually becomes a running application on your machine:</p><pre><code class="language-mermaid">flowchart TD
    %% Custom Styling
    classDef source fill:#1e1e2e,stroke:#f38ba8,stroke-width:2px,color:#cdd6f4;
    classDef recipe fill:#1e1e2e,stroke:#f9e2af,stroke-width:2px,color:#cdd6f4;
    classDef repository fill:#1e1e2e,stroke:#a6e3a1,stroke-width:2px,color:#cdd6f4;
    classDef build fill:#1e1e2e,stroke:#89b4fa,stroke-width:2px,color:#cdd6f4;
    classDef final fill:#11111b,stroke:#a6e3a1,stroke-width:3px,color:#a6e3a1;

    %% Elements
    A[&quot;🌐 Upstream Source Code&lt;br/&gt;(GitHub, GitLab, tar.gz)&quot;]:::source
    
    subgraph AUR_Directory [&quot;🐙 Arch User Repository (AUR)&quot;]
        B[&quot;📄 PKGBUILD Script&lt;br/&gt;(Text instructions)&quot;]:::recipe
    end
    
    C[&quot;🛠️ AUR Helper (paru / yay)&quot;]:::build
    D[&quot;⚙️ makepkg Tool&lt;br/&gt;(Local Compilation)&quot;]:::build
    E[&quot;📦 Built Arch Package&lt;br/&gt;(*.pkg.tar.zst)&quot;]:::build
    F[&quot;🖥️ Target System&lt;br/&gt;(Installed via pacman)&quot;]:::final

    %% Flow
    B --&gt;|1. paru pulls recipe| C
    A --&gt;|2. Downloads code| C
    C --&gt;|3. Feeds code &amp; recipe| D
    D --&gt;|4. Compiles &amp; packages| E
    E --&gt;|5. Installs binary| F
</code></pre><hr /><h2 id="deep-dive:-pkgbuild-vs.-aur-vs.-cachyos">🔍 Deep Dive: PKGBUILD vs. AUR vs. CachyOS</h2><h3 id="1.-what-is-a-pkgbuild?">1. What is a PKGBUILD?</h3><p>A <code>PKGBUILD</code> is a plain-text shell script containing variables and functions (like <code>prepare()</code>, <code>build()</code>, and <code>package()</code>).</p><p>Here is a simplified look at what a <code>PKGBUILD</code> does:</p><pre><code class="language-bash">pkgname=freebuff-bin
pkgver=0.0.106
arch=(&apos;x86_64&apos;)
depends=(&apos;glibc&apos;)

source=(&quot;https://github.com/CodebuffAI/codebuff/releases/download/v${pkgver}/freebuff-linux&quot;)

package() {
    # Install the precompiled binary into the system binary path
    install -Dm755 &quot;${srcdir}/freebuff-linux&quot; &quot;${pkgdir}/usr/bin/freebuff&quot;
}
</code></pre><h3 id="2.-is-the-aur-full-of-binary-files?">2. Is the AUR Full of Binary Files?</h3><p><strong>No.</strong> The AUR website only hosts <strong>text-based PKGBUILD scripts</strong> (along with occasional helper configuration files). It does not store <code>.exe</code>, <code>.bin</code>, or <code>.pkg.tar.zst</code> packages.</p><p>However, some package names on the AUR end with <code>-bin</code> (e.g., <code>freebuff-bin</code>). This is a naming convention:</p><ul><li><strong><code>freebuff</code> (Source package)</strong>: The PKGBUILD downloads the raw C/Rust/Go source code, compiles it on your machine (which can take minutes or hours), and packages the result.</li><li><strong><code>freebuff-bin</code> (Binary package)</strong>: The PKGBUILD skips compilation by downloading a pre-built binary compiled by the original developers (e.g., from their GitHub Releases page) and packages it instantly.</li></ul><h3 id="3.-why-does-cachyos-maintain-its-own-pkgbuild-repository?">3. Why does CachyOS maintain its own PKGBUILD repository?</h3><p>CachyOS is a downstream distribution focused on squeezing out every drop of performance from modern hardware. Instead of fetching generic packages from Arch Linux or compilation scripts from the AUR, they maintain a fork called <code>CachyOS-PKGBUILDS</code>.</p><p>They do this to implement three key enhancements:</p><ol start="1"><li><strong>Instruction Set Architecture (ISA) Optimization</strong>: Standard Arch Linux packages are built for generic <code>x86-64</code> CPUs (compatible with older computers). CachyOS rebuilds these packages targeting <strong>x86-64-v3</strong> (which requires AVX2) and <strong>x86-64-v4</strong> (AVX-512). This makes CPU-bound software significantly faster.</li><li><strong>Custom Performance Patches</strong>: CachyOS injects patches directly into their PKGBUILDs—such as customized kernel patches (<code>linux-cachyos</code>), CPU scheduler configurations (<code>scx</code> for sched-ext), and custom compiler flags (<code>-O3</code>, LTO).</li><li><strong>Stability &amp; Mirrors</strong>: By compiling their own PKGBUILDs on dedicated build servers, CachyOS hosts pre-compiled, optimized binaries on their official package mirrors. This gives users optimized software without forcing them to spend hours compiling it locally.</li></ol><hr /><h2 id="comparison-with-traditional-distributions-(debian-&amp;-red-hat)">🌎 Comparison with Traditional Distributions (Debian &amp; Red Hat)</h2><p>If you are coming from a Debian-based (Ubuntu, Mint) or Red Hat-based (Fedora, RHEL) background, the Arch approach feels very different. Traditional distributions handle packaging and third-party software using different philosophies:</p><h3 id="1.-debian-/-ubuntu-(apt-&amp;-.deb)">1. Debian / Ubuntu (Apt &amp; <code>.deb</code>)</h3><ul><li><strong>How they distribute</strong>: Pre-compiled binaries (<code>.deb</code> files) hosted in large central repositories.</li><li><strong>Third-Party / Community Apps</strong>: Instead of a single repository for community recipes, Debian/Ubuntu uses <strong>PPAs (Personal Package Archives)</strong>. A PPA is a separate hosted repository of actual pre-compiled binary packages.</li><li><strong>Building Packages</strong>: Creating a <code>.deb</code> package requires a complex file structure (including a <code>debian/control</code> file, <code>debian/rules</code> file, etc.) and utilities like <code>dpkg-buildpackage</code>. It is significantly more difficult to write than a single <code>PKGBUILD</code> script.</li></ul><h3 id="2.-red-hat-/-fedora-(dnf-&amp;-.rpm)">2. Red Hat / Fedora (DNF &amp; <code>.rpm</code>)</h3><ul><li><strong>How they distribute</strong>: Pre-compiled binaries (<code>.rpm</code> files).</li><li><strong>Third-Party / Community Apps</strong>: Fedora uses <strong>COPR</strong> (Cool Other Package Repo), similar to Ubuntu&apos;s PPAs. It hosts pre-compiled binaries built by users on Red Hat’s servers.</li><li><strong>Building Packages</strong>: Creating an <code>.rpm</code> package requires writing an RPM <code>.spec</code> file. While structured, spec files are notoriously verbose and complex compared to the clean, Bash-like syntax of <code>PKGBUILD</code>s.</li></ul><h3 id="summary-of-differences">Summary of Differences</h3><ul><li><strong>Source vs. Binary Community Repos</strong>: The <strong>AUR</strong> distributes <strong>scripts</strong> (you compile locally). <strong>PPAs &amp; COPR</strong> distribute <strong>pre-compiled binaries</strong> (compiled on canonical servers and downloaded directly).</li><li><strong>Simplicity</strong>: Arch&apos;s <code>PKGBUILD</code> is a single, readable Bash script. Debian and Red Hat packages require complex build directories and specialized spec/rule syntaxes.</li><li><strong>Optimization</strong>: Debian and RHEL prioritize stability and compatibility, targeting generic, older <code>x86-64</code> hardware. CachyOS rebuilds everything specifically to leverage modern CPU instructions.</li></ul><h3 id="visual-comparison:-aur-vs.-ppa/copr-build-models">Visual Comparison: AUR vs. PPA/COPR Build Models</h3><pre><code class="language-mermaid">flowchart TD
    %% Custom Styling
    classDef arch fill:#1e1e2e,stroke:#89b4fa,stroke-width:2px,color:#cdd6f4;
    classDef debian fill:#1e1e2e,stroke:#f38ba8,stroke-width:2px,color:#cdd6f4;
    classDef compile fill:#1e1e2e,stroke:#f9e2af,stroke-width:2px,color:#cdd6f4;
    classDef system fill:#11111b,stroke:#a6e3a1,stroke-width:3px,color:#cdd6f4;

    subgraph Arch_Model [&quot;Arch Linux (AUR) Model&quot;]
        A1[&quot;📄 AUR Server&lt;br/&gt;(Hosts PKGBUILD Script)&quot;]:::arch
        A2[&quot;🌐 Upstream Source Code&lt;br/&gt;(GitHub / Tarball)&quot;]:::arch
        A3[&quot;⚙️ User&apos;s Machine&lt;br/&gt;(Compiles &amp; Packages)&quot;]:::compile
        A4[&quot;🖥️ Installed Program&quot;]:::system

        A1 --&gt;|1. Downloads script| A3
        A2 --&gt;|2. Downloads code| A3
        A3 --&gt;|3. Install package| A4
    end

    subgraph Debian_RedHat_Model [&quot;Debian (PPA) / RedHat (COPR) Model&quot;]
        B1[&quot;📄 Recipe / Spec File&quot;]:::debian
        B2[&quot;🌐 Upstream Source Code&quot;]:::debian
        B3[&quot;☁️ Build Servers&lt;br/&gt;(Canonical/RedHat Cloud)&quot;]:::compile
        B4[&quot;📦 Hosted Binary Repository&lt;br/&gt;(.deb / .rpm)&quot;]:::debian
        B5[&quot;⚙️ User&apos;s Machine&lt;br/&gt;(Apt / DNF Download)&quot;]:::compile
        B6[&quot;🖥️ Installed Program&quot;]:::system

        B1 --&gt;|1. Build trigger| B3
        B2 --&gt;|2. Upload code| B3
        B3 --&gt;|3. Compile &amp; store| B4
        B4 --&gt;|4. Download binary| B5
        B5 --&gt;|5. Install| B6
    end
</code></pre><hr /><h2 id="comparison-table">📊 Comparison Table</h2><table><thead><tr><th style="text-align:left;">Feature</th><th style="text-align:left;">Upstream Code / Release</th><th style="text-align:left;">PKGBUILD Script</th><th style="text-align:left;">AUR (Arch User Repo)</th><th style="text-align:left;">CachyOS PKGBUILD Repo</th></tr></thead><tbody><tr><td style="text-align:left;"><strong>What is it?</strong></td><td style="text-align:left;">Raw application source code or generic binary releases.</td><td style="text-align:left;">A text file containing build instructions.</td><td style="text-align:left;">A community directory of PKGBUILD scripts.</td><td style="text-align:left;">A curated collection of CachyOS-specific PKGBUILDs.</td></tr><tr><td style="text-align:left;"><strong>Host Location</strong></td><td style="text-align:left;">GitHub, GitLab, official websites.</td><td style="text-align:left;">Local filesystem (e.g., <code>/tmp</code>).</td><td style="text-align:left;"><code>aur.archlinux.org</code></td><td style="text-align:left;"><code>github.com/CachyOS/CachyOS-PKGBUILDS</code></td></tr><tr><td style="text-align:left;"><strong>Content Type</strong></td><td style="text-align:left;"><code>.tar.gz</code>, <code>.zip</code>, <code>.rs</code>, <code>.cpp</code>, <code>.go</code></td><td style="text-align:left;">Plain text shell script (<code>bash</code> syntax).</td><td style="text-align:left;">Git repositories containing only PKGBUILD files.</td><td style="text-align:left;">Git repository containing CachyOS package build scripts.</td></tr><tr><td style="text-align:left;"><strong>Compilation</strong></td><td style="text-align:left;">N/A (unbuilt source).</td><td style="text-align:left;">Happens when you run <code>makepkg</code> on it.</td><td style="text-align:left;">User compiles it locally (unless it&apos;s a <code>-bin</code> package).</td><td style="text-align:left;">Pre-compiled on CachyOS servers; distributed via pacman mirrors.</td></tr><tr><td style="text-align:left;"><strong>Optimizations</strong></td><td style="text-align:left;">Generic.</td><td style="text-align:left;">Up to the user&apos;s local configuration.</td><td style="text-align:left;">Generic.</td><td style="text-align:left;">Highly optimized (AVX2, AVX-512, <code>-O3</code>, LTO).</td></tr></tbody></table><hr /><h2 id="practical-takeaway">🛠️ Practical Takeaway</h2><p>When you want to run new software like <strong>Freebuff</strong> (a terminal-based AI coding assistant):</p><ul><li>You check if it is in the official repositories or <code>CachyOS-PKGBUILDS</code>.</li><li>If it isn&apos;t, you use an AUR helper like <code>paru</code> (e.g., <code>paru -S freebuff-bin</code>).</li><li><code>paru</code> reads the recipe from the AUR, downloads the binary release from upstream, and installs it cleanly on your system.</li></ul></div>]]></content>
  </entry>
  <entry>
    <id>https://blog.nurazhar.com/how-to-maximize-clojure-usage-token-on-antigravity-ide.html</id>
    <link href="https://blog.nurazhar.com/how-to-maximize-clojure-usage-token-on-antigravity-ide.html"/>
    <title>How to Maximize Clojure Usage &amp; Minimize Token Quota on Antigravity IDE</title>
    <updated>2026-06-14T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><hr /><p>Developing Clojure applications with AI assistance introduces a unique challenge: Clojure namespaces are dense, data structures are deeply nested, and code-as-data constructs demand precise context. If you feed entire namespaces and REPL outputs to reasoning-focused LLMs without a strategy, you will burn through your token quotas (and API budget) rapidly.</p><p>Here is how to optimize your developer workflow in <strong>Antigravity IDE</strong> using Gemini&apos;s <strong>Thinking Budget</strong> configurations.</p><hr /><h2 id="understanding-the-reasoning-settings-(thinking-levels)">🧠 Understanding the Reasoning Settings (Thinking Levels)</h2><p>Modern models (like Gemini 2.5/3.5 Flash and Pro) introduce a configurable <strong>Thinking Level</strong> (or Reasoning Budget). This setting represents how many internal, step-by-step reasoning tokens the model compiles before writing the final code.</p><p>To manage your tokens, you need to understand when to toggle between them:</p><table><thead><tr><th style="text-align:left;">Setting</th><th style="text-align:left;">When to Use</th><th style="text-align:left;">Clojure Dev Tasks</th><th style="text-align:left;">Quota Consumption</th><th style="text-align:left;">Latency</th></tr></thead><tbody><tr><td style="text-align:left;"><strong>Low / Off</strong></td><td style="text-align:left;">Simple, rote coding and direct translations</td><td style="text-align:left;">Boilerplate templating, converting JSON to edn, writing basic Babashka shell scripts.</td><td style="text-align:left;"><strong>Minimal</strong> (Input + Output only)</td><td style="text-align:left;"><strong>Very Fast</strong></td></tr><tr><td style="text-align:left;"><strong>Medium</strong></td><td style="text-align:left;">Logical, multi-step coding problems</td><td style="text-align:left;">Standard app functions, writing test suites, refactoring standard functions.</td><td style="text-align:left;"><strong>Moderate</strong></td><td style="text-align:left;"><strong>Moderate</strong></td></tr><tr><td style="text-align:left;"><strong>High</strong></td><td style="text-align:left;">Highly complex, stateful, or abstract logic</td><td style="text-align:left;">Designing advanced macros, debugging race conditions in <code>core.async</code> pipelines, orchestrating multi-agent systems.</td><td style="text-align:left;"><strong>Maximum</strong> (Adds hidden thinking tokens)</td><td style="text-align:left;"><strong>Slower</strong></td></tr></tbody></table><hr /><h2 id="tactical-rules-for-clojure-token-preservation">🛠️ Tactical Rules for Clojure Token Preservation</h2><h3 id="1.-repl-driven-prompting-(don&apos;t-copy-paste-entire-files)">1. REPL-Driven Prompting (Don&apos;t Copy-Paste Entire Files)</h3><p>Instead of copy-pasting your entire <code>core.clj</code> namespace to fix an error:</p><ol start="1"><li>Copy <strong>only</strong> the function signature and its spec (if you use <code>clojure.spec</code>).</li><li>Paste the exact stack trace from your REPL.</li><li>Keep the thinking level on <strong>Low</strong>. The model doesn&apos;t need to reason about your whole system to fix a syntax bug or structural partition mismatch.</li></ol><h3 id="2.-guard-against-the-chat-history-multiplier">2. Guard Against the Chat History Multiplier</h3><p>In interactive chats, the IDE sends your entire chat history back to the model on every new turn. If your previous prompts returned responses that consumed <strong>2,000 thinking tokens</strong>, those tokens are sent back as input on the next turn.</p><ul><li><strong>The Fix:</strong> Treat chat threads as transient. Once a bug is resolved or a concept is clear, close the chat thread and start a fresh one. This resets your input context back to zero.</li></ul><h3 id="3.-programmatic-control-via-babashka">3. Programmatic Control via Babashka</h3><p>If you write automation scripts that query the Gemini API directly, you can configure the thinking budget dynamically in your payload using <code>generationConfig</code>.</p><p>Here is a Babashka script template showing how to switch thinking budgets dynamically:</p><pre><code class="language-clojure">#!/usr/bin/env bb
(require &apos;[babashka.http-client :as http]
         &apos;[cheshire.core :as json])

(def api-key (System/getenv &quot;GEMINI_API_KEY&quot;))

(defn query-gemini [prompt thinking-budget]
  ;; thinking-budget = 0 (Minimal/Low)
  ;; thinking-budget = 1024 or 2048 (Medium/High reasoning token limit)
  (let [url (str &quot;https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=&quot; api-key)
        payload {:contents [{:parts [{:text prompt}]}]
                 :generationConfig 
                 (merge 
                  {:temperature 1.0}
                  (if (zero? thinking-budget)
                    {:thinkingConfig {:thinkingBudget 0}}
                    {:thinkingConfig {:thinkingBudget thinking-budget}}))}]
    (-&gt; (http/post url {:headers {&quot;Content-Type&quot; &quot;application/json&quot;}
                        :body (json/generate-string payload)})
        :body
        (json/parse-string true)
        :candidates
        first
        :content
        :parts
        first
        :text)))

;; Example: Run a low-cost, fast query
(println (query-gemini &quot;Generate a basic Babashka task definition for file-syncing.&quot; 0))
</code></pre><hr /><h2 id="conclusion">🎯 Conclusion</h2><p>Treating LLM usage like hardware memory management makes you a highly efficient developer:</p><ul><li>Use <strong>Low</strong> by default for 90% of your coding and rapid chatting.</li><li>Elevate to <strong>High</strong> reasoning exclusively for architectural bottlenecks or macro debugging.</li><li>Keep your chat history short.</li></ul></div>]]></content>
  </entry>
  <entry>
    <id>https://blog.nurazhar.com/quickblog-double-title-gotcha.html</id>
    <link href="https://blog.nurazhar.com/quickblog-double-title-gotcha.html"/>
    <title>Quickblog Gotcha: Why Your Post Titles Are Displaying Twice</title>
    <updated>2026-06-14T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><hr /><h1 id="quickblog-gotcha:-why-your-post-titles-are-displaying-twice">Quickblog Gotcha: Why Your Post Titles Are Displaying Twice</h1><p>When migrating articles from other static site generators (like Hugo, Jekyll, or standard GitHub-flavored Markdown) to <strong><a href="https://github.com/borkdude/quickblog">Quickblog</a></strong>, you might run into a jarring visual bug: your post titles show up twice at the top of the page.</p><p>Here is why it happens, and how to programmatically fix it across your entire codebase.</p><hr /><h2 id="the-root-cause:-template-vs.-body">The Root Cause: Template vs. Body</h2><p>In standard Markdown styling, it is natural to start your file with an H1 header matching your title:</p><pre><code class="language-markdown">Title: My Great Post
Date: 2026-06-14
---
# My Great Post

This is the content of my post...
</code></pre><p>However, Quickblog uses a rendering template (typically found in <code>templates/post.html</code>) to dynamically construct your posts. If you inspect the default template, you will see it already wraps your metadata <code>{{title}}</code> in an H1 block:</p><pre><code class="language-html">&lt;h1&gt;
  {% if post-link %}&lt;a href=&quot;{{post-link}}&quot;&gt;{% endif %}
  {{title}}
  {% if post-link %}&lt;/a&gt;{% endif %}
&lt;/h1&gt;
{{body | safe}}
</code></pre><p>Because Quickblog inserts the title dynamically, any <code># Title</code> header you manually write inside the Markdown body will result in a double title on the compiled HTML page.</p><hr /><h2 id="the-fix:-programmatic-refactoring">The Fix: Programmatic Refactoring</h2><p>Instead of manually editing dozens of markdown files to delete the redundant H1 header, you can clean them all up instantly using a quick Python regex script.</p><p>Run this command in your repository root:</p><pre><code class="language-bash">python3 -c &apos;
import glob, re
for f in glob.glob(&quot;posts/*.md&quot;):
    with open(f, &quot;r&quot;) as file:
        content = file.read()
    # Remove the first H1 header line and any trailing empty lines immediately after it
    new_content = re.sub(r&quot;(?m)^# .*\n\n?&quot;, &quot;&quot;, content, count=1)
    with open(f, &quot;w&quot;) as file:
        file.write(new_content)
&apos;
</code></pre><p>This script:</p><ol start="1"><li>Loops through every Markdown file in your <code>posts/</code> folder.</li><li>Uses a regex pattern (<code>(?m)^# .*\n\n?</code>) to find only the first occurrence of an H1 header (<code># </code>) at the beginning of a line.</li><li>Removes it along with any trailing blank lines.</li><li>Rewrites the file, leaving your metadata header and standard body intact.</li></ol><p>Once completed, run <code>bb quickblog render</code> to rebuild the pages, and the layout will render perfectly with a single title block.</p></div>]]></content>
  </entry>
  <entry>
    <id>https://blog.nurazhar.com/quickblog-minimalist-architecture.html</id>
    <link href="https://blog.nurazhar.com/quickblog-minimalist-architecture.html"/>
    <title>Quickblog: The Zen of Minimalist Static Site Generation</title>
    <updated>2026-06-14T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><hr /><p>When building a personal blog, modern web development defaults to overkill. Setting up dynamic frameworks (like Next.js, Remix, or MERN stacks) with backend databases, authentication layers, and real-time APIs introduces a massive maintenance burden, security attack surface, and slow page-load times.</p><p>For a blog, <strong>static HTML is the ultimate architecture</strong>. This blog is powered by <strong><a href="https://github.com/borkdude/quickblog">Quickblog</a></strong>, a lightweight static site generator designed for the Clojure/Babashka ecosystem by Michiel Borkent (@borkdude).</p><p>Here is a breakdown of how it works under the hood and why this architecture is a superior design choice.</p><hr /><h2 id="1.-the-core-runtime:-babashka">1. The Core Runtime: Babashka</h2><p>At the heart of Quickblog is <strong><a href="https://babashka.org/">Babashka</a></strong>, a fast-starting scripting runtime for Clojure.</p><p>Traditional Clojure runs on the JVM, which is powerful but suffers from a slow startup time (often several seconds)—making it tedious for quick CLI utilities and scripts. Babashka solves this by using GraalVM to compile the Clojure interpreter into a native binary. This gives us:</p><ul><li><strong>Instant startup</strong> (measured in milliseconds).</li><li><strong>Zero JVM overhead</strong> on the host.</li><li>A rich subset of Clojure libraries (like shell execution, filesystem navigation, and HTTP client/server) pre-baked into the runtime.</li></ul><p>Quickblog is executed entirely as a Babashka task defined in <code>bb.edn</code>.</p><hr /><h2 id="2.-compilation-flow">2. Compilation Flow</h2><p>Quickblog&apos;s architecture follows a clean, functional pipeline:</p><pre><code class="language-mermaid">graph TD
    A[Markdown Posts /posts] --&gt; B(Babashka CLI / bb.edn)
    C[HTML Templates /templates] --&gt; B
    D[Assets /assets] --&gt; B
    B --&gt;|bb quickblog render| E[Pure HTML/CSS/RSS /public]
    E --&gt;|Git Push| F[GitHub Pages]
    F --&gt;|HTTPS CNAME| G(blog.nurazhar.com)
</code></pre><ol start="1"><li><strong>Ingestion</strong>: It reads raw Markdown files from the <code>/posts</code> directory and reads configuration parameters from <code>bb.edn</code>.</li><li><strong>Metadata Parsing</strong>: It extracts frontmatter metadata (Title, Date, Tags, Description) from the top of each Markdown file.</li><li><strong>HTML Transformation</strong>: Using Clojure-based parsing engines, it transforms Markdown syntax into clean HTML.</li><li><strong>Templating</strong>: It uses <strong><a href="https://github.com/yogthos/Selmer">Selmer</a></strong> (a Django-like templating library written in Clojure) to wrap the rendered Markdown inside your main HTML layout (<code>/templates/base.html</code>, <code>/templates/post.html</code>, etc.).</li><li><strong>Feed &amp; Index Generation</strong>: It automatically aggregates posts sorted by date to generate:<ul><li><code>index.html</code> (the homepage loading up to <span class="formula">N</span> posts).</li><li><code>archive.html</code> (a list of all posts).</li><li><code>tags.html</code> (filtered views).</li><li><code>feed.xml</code> (RSS feed).</li></ul></li><li><strong>Output</strong>: All static resources are placed into a single <code>/public</code> directory, ready to be hosted anywhere.</li></ol><hr /><h2 id="3.-why-this-design-is-superior-(the-pros)">3. Why This Design is Superior (The Pros)</h2><ul><li><strong>Zero-Database Security</strong>: No dynamic database queries mean <strong>zero SQL injection, zero Server-Side Request Forgery (SSRF), and zero database connection exhaustion</strong>.</li><li><strong>Extreme Performance</strong>: Static HTML files are served directly by the web server (GitHub Pages / Nginx / Cloudflare) at edge speeds. The browser parses only clean HTML and CSS, rendering instantly.</li><li><strong>Low Footprint</strong>: No background servers running on Node.js or Python, minimizing CPU/Memory usage.</li><li><strong>Git as the Single Source of Truth</strong>: Posts are stored as plain text version-controlled files. Moving, backing up, or editing posts is as simple as running standard Git commands.</li></ul><hr /><h2 id="conclusion:-emulating-satoshi&apos;s-design-philosophy">Conclusion: Emulating Satoshi&apos;s Design Philosophy</h2><p>In his original 2008 Bitcoin v0.1 release, Satoshi Nakamoto focused on <strong>peer-to-peer simplicity</strong>—designing a system with direct nodes, no unnecessary intermediate layers, and raw efficiency.</p><p>Quickblog mirrors this philosophy in web development. By stripping away heavy JS frameworks, hydrations, client-side state managers, and database servers, it leaves you with exactly what the web was built for: <strong>clean, readable, and immortal HTML</strong>.</p></div>]]></content>
  </entry>
  <entry>
    <id>https://blog.nurazhar.com/15-minute-serverless-vapt.html</id>
    <link href="https://blog.nurazhar.com/15-minute-serverless-vapt.html"/>
    <title>The 15-Minute VAPT: Zero-Backend Security on Neon and GCP</title>
    <updated>2026-06-14T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><hr /><p>In enterprise environments (like Singapore&apos;s defense and corporate sectors), <strong>Vulnerability Assessment and Penetration Testing (VAPT)</strong> is typically a multi-week bureaucratic slog. You pay a consultant $20,000+, they run standard scanner suites, generate a bloated 50-page PDF report of generic warnings, and hand it back to your developers to spend three months debating and patching.</p><p>It is a high-overhead, low-velocity cycle.</p><p>But when you build using a <strong>&quot;No-Backend&quot; architecture</strong>—eliminating custom middleware servers in favor of static edge clients, serverless database triggers, and a stateless proxy—the attack surface shrinks so dramatically that you can perform a thorough VAPT, write custom sanitizers, harden schema constraints, and redeploy to production in <strong>less than 15 minutes</strong>.</p><p>Here is how we did exactly that for <code>lagu-lagu</code> (a real-time Singapore PayNow payout registry for independent artists).</p><hr /><h2 id="the-minimalist-surface-area">🏗️ The Minimalist Surface Area</h2><p>Our application uses a zero-server runtime model:</p><ol start="1"><li><strong>Frontend:</strong> Static HTML and <strong>HTMX</strong> served from GitHub Pages (100% static CDN).</li><li><strong>Database:</strong> Serverless <strong>Neon Postgres</strong>, with Row-Level Security (RLS) enabled and a PL/pgSQL database trigger calculating the 80/20 split on payment insertion.</li><li><strong>API Gateway:</strong> A stateless <strong>GCP Cloud Function (Node.js)</strong> that secures connection strings, handles incoming webhooks, and returns pre-rendered HTML snippets.</li></ol><p>By deleting standard backend servers (like Spring Boot or Express), we deleted 99% of our dependency overhead. We don&apos;t have server routes to exploit, memory leaks to exploit, or unpatched framework packages.</p><p>But minimalist doesn&apos;t mean bulletproof. We audited the system and found three real vulnerabilities.</p><hr /><h2 id="the-vapt-checklist-&amp;-exploits">🔍 The VAPT Checklist &amp; Exploits</h2><h3 id="1.-stored-xss-via-html-template-injection-(high-severity)">1. Stored XSS via HTML Template Injection (High Severity)</h3><p>Our GCP function was querying the database and generating HTML string templates dynamically to serve back to the HTMX frontend:</p><pre><code class="language-javascript">const html = rows.map(artist =&gt; `&lt;h3&gt;${artist.name}&lt;/h3&gt;...`).join(&quot;&quot;);
</code></pre><ul><li><strong>The Exploit:</strong> If an attacker registered an artist name containing a malicious script tag (e.g., <code>&lt;script&gt;steal(document.cookie)&lt;/script&gt;</code>), Postgres would store it, the GCP function would fetch it, and the browser would execute it.</li><li><strong>The Fix:</strong> We wrote a lightweight, native HTML escaping function inside Node.js and wrapped all dynamic SQL variables before concatenation:<pre><code class="language-javascript">function escapeHtml(unsafe) {
  if (!unsafe) return &apos;&apos;;
  return unsafe.toString()
    .replace(/&amp;/g, &quot;&amp;amp;&quot;)
    .replace(/&lt;/g, &quot;&amp;lt;&quot;)
    .replace(/&gt;/g, &quot;&amp;gt;&quot;)
    .replace(/&quot;/g, &quot;&amp;quot;&quot;)
    .replace(/&apos;/g, &quot;&amp;#039;&quot;);
}
</code></pre></li></ul><h3 id="2.-lack-of-db-constraints-on-payment-splits-(low-severity)">2. Lack of DB Constraints on Payment Splits (Low Severity)</h3><p>The split trigger calculated the 80/20 division using standard multiplication (<code>NEW.amount * 0.80</code>). But the schema did not explicitly validate that payment amounts were positive.</p><ul><li><strong>The Exploit:</strong> If a negative transaction amount (e.g. <code>SGD -10.00</code>) bypassed frontend validation or was injected via a rogue webhook, the database would generate a negative artist payout (<code>SGD -8.00</code>), causing financial ledger logic corruption.</li><li><strong>The Fix:</strong> We executed raw SQL queries to add constraints directly to the live Neon instance:<pre><code class="language-sql">ALTER TABLE transactions ADD CONSTRAINT check_positive_amount CHECK (amount &gt; 0);
ALTER TABLE payouts ADD CONSTRAINT check_positive_payout CHECK (amount_sent &gt; 0);
</code></pre></li></ul><h3 id="3.-error-verbosity-leak-(low-severity)">3. Error Verbosity Leak (Low Severity)</h3><p>In our try-catch block, we returned raw stack errors to the client:</p><pre><code class="language-javascript">catch (error) {
  return res.status(500).json({ error: error.message });
}
</code></pre><ul><li><strong>The Exploit:</strong> If a query crashed, Postgres table names, column structures, and connection parameters would leak directly in the browser console.</li><li><strong>The Fix:</strong> We redirected all detailed traces to GCP Cloud Logging and genericized the HTTP client response to a sanitized string:<pre><code class="language-javascript">catch (error) {
  console.error(&quot;Internal API error:&quot;, error);
  return res.status(500).json({ error: &quot;Internal Server Error&quot; });
}
</code></pre></li></ul><hr /><h2 id="proactive-prevention:-github-push-protection">🔒 Proactive Prevention: GitHub Push Protection</h2><p>To ensure we never leak a connection string or API token in the future, we used the GitHub CLI to globally configure security settings for our repos:</p><pre><code class="language-bash">echo &apos;{&quot;security_and_analysis&quot;:{&quot;secret_scanning&quot;:{&quot;status&quot;:&quot;enabled&quot;},&quot;secret_scanning_push_protection&quot;:{&quot;status&quot;:&quot;enabled&quot;}}}&apos; \
  | gh api --method PATCH repos/nurazhardotcom/lagu-lagu --input -
</code></pre><p>With <strong>Push Protection</strong> enabled, GitHub will intercept and block any git push command at the CLI layer if it detects an exposed password or token before it is committed to the cloud.</p><hr /><h2 id="the-security-lesson">💡 The Security Lesson</h2><p>Modern security compliance (like Singapore&apos;s Cyber Trust Mark or corporate checklists) tends to focus on administrative processes, PDF paperwork, and constant scanning.</p><p>But <strong>complexity is the ultimate enemy of security</strong>.</p><p>The safest line of code is the one you never wrote. By keeping your backend database-native, relying on Postgres constraints for business logic, and routing calls via stateless Cloud Functions, you keep your system so simple that VAPT goes from a multi-week corporate bottleneck to a 15-minute engineering sprint.</p></div>]]></content>
  </entry>
  <entry>
    <id>https://blog.nurazhar.com/the-autonomous-os-safety-net-for-agentic-sysops.html</id>
    <link href="https://blog.nurazhar.com/the-autonomous-os-safety-net-for-agentic-sysops.html"/>
    <title>The Autonomous OS: Btrfs, Snapper, and the Safety Net for Agentic SysOps</title>
    <updated>2026-06-14T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><hr /><p>As of mid-2026, the tech industry has reached a quiet consensus: <strong>AI agents cannot safely manage servers using raw bash terminal access.</strong></p><p>LLMs are probabilistic. They guess the next best token. While a 99% accuracy rate is great for writing marketing copy, a 1% failure rate in system operations (SysOps/LMOps) can mean deleting database volumes, bricking configurations, or corrupting boot partitions.</p><p>So how do we give AI agents the keys to the kingdom without risking total disaster? We build a legacy safety net.</p><p>This post explores the concepts from our deep dive into the <strong>openSUSE Autonomous OS pathway</strong>—specifically, how openSUSE is pairing futuristic AI standard protocols with battle-tested filesystem technologies to build closed-loop self-healing systems.</p><hr /><h2 id="the-core-metaphor:-from-brain-to-machine">🚗 The Core Metaphor: From Brain to Machine</h2><p>To understand how AI interacts with an operating system today, we can break the architecture down into four layers using a simple <strong>Car, Steering Wheel, and Driver</strong> analogy:</p><pre><code class="language-mermaid">graph TD
    A[&quot;🧠 1. The App / Client (The Driver) &lt;br&gt;Cursor, Claude, Antigravity Client&quot;] --&gt;|Sends MCP commands| B[&quot;🛞 2. Model Context Protocol (The Steering Wheel) &lt;br&gt;Standardized Tool-Calling Protocol&quot;]
    B --&gt;|Translates to local shell| C[&quot;⚙️ 3. The MCP Server (The ECU / Hands) &lt;br&gt;Local python/node daemon translating commands&quot;]
    C --&gt;|Executes filesystem changes| D[&quot;🏎️ 4. The Operating System (The Engine) &lt;br&gt;openSUSE, Btrfs, Snapper, Hardware&quot;]
</code></pre><ol start="1"><li><strong>The App / Client (The Driver):</strong> The AI brain (often running in the cloud or local workstation). It is smart, but has no physical hands. It cannot type or edit local files directly; it only outputs text.</li><li><strong>Model Context Protocol / MCP (The Steering Wheel):</strong> The open-source standard connecting the AI to your computer. Just like USB-C standardized physical hardware connections, MCP standardizes how AI models call tools.</li><li><strong>The MCP Server (The Engine Control Unit / The Hands):</strong> A lightweight background program running on your host machine. It translates the standardized tool-calling instructions from the AI into physical actions (e.g. running a bash script or editing a config).</li><li><strong>The Operating System (The Engine):</strong> The underlying platform (Linux, Btrfs, Snapper, hardware) that performs the physical disk writes and computes the changes.</li></ol><hr /><h2 id="the-four-enterprise-pathways">🗺️ The Four Enterprise Pathways</h2><p>In 2026, different enterprise Linux vendors are approaching AI SysOps from unique angles:</p><table><thead><tr><th style="text-align:left;">Distribution</th><th style="text-align:left;">Core Strategy</th><th style="text-align:left;">Key Technologies</th><th style="text-align:left;">Ideal Workloads</th></tr></thead><tbody><tr><td style="text-align:left;"><strong>openSUSE / SUSE</strong></td><td style="text-align:left;"><strong>The Self-Healing Loop</strong></td><td style="text-align:left;">Btrfs + Snapper + <code>transactional-update</code></td><td style="text-align:left;">Autonomous mitigation, self-updating servers, write-capable AI agents.</td></tr><tr><td style="text-align:left;"><strong>Red Hat / RHEL</strong></td><td style="text-align:left;"><strong>Telemetry &amp; Diagnostics</strong></td><td style="text-align:left;"><code>linux-mcp-server</code> + systemd logs</td><td style="text-align:left;">Compliance-heavy environments, read-only AI audits, remote troubleshooting.</td></tr><tr><td style="text-align:left;"><strong>Amazon Linux</strong></td><td style="text-align:left;"><strong>Cloud Scaling</strong></td><td style="text-align:left;">AWS Agent Toolkit + Amazon Q</td><td style="text-align:left;">Cloud-native microservices, ECS clustering, automated infrastructure scaling.</td></tr><tr><td style="text-align:left;"><strong>Canonical / Ubuntu</strong></td><td style="text-align:left;"><strong>Sovereignty &amp; Local AI</strong></td><td style="text-align:left;">Local LLMs + Snap/Flatpak sandboxes</td><td style="text-align:left;">Offline developer setups, on-device training, high-privacy data processing.</td></tr></tbody></table><hr /><h2 id="the-opensuse-solution:-btrfs-&amp;-snapper-as-a-safety-net">🛡️ The openSUSE Solution: Btrfs &amp; Snapper as a Safety Net</h2><p>SUSE&apos;s key advantage is that they didn&apos;t write new, experimental code to protect systems from AI hallucinations. Instead, they leveraged their <strong>decade-old, bulletproof filesystem technology: Btrfs and Snapper</strong>.</p><p>This creates the <strong>Self-Healing Loop</strong>:</p><pre><code class="language-text">  [ AI Agent ] 
       │
       ├─► 1. Save Checkpoint (AI triggers Snapper snapshot e.g. Snapshot #100)
       ├─► 2. Execute Action (AI runs system upgrade or writes configuration)
       ├─► 3. Validate (AI runs post-change diagnostic tests)
       │
       ├───► If SUCCESS: Keep changes.
       └───► If FAILURE: Trigger Snapper rollback to Snapshot #100.
</code></pre><p>If the AI makes a destructive change, the entire OS is rolled back to the clean snapshot instantaneously. Because <code>/home</code> is typically kept on a separate subvolume/partition, the system is restored to safety <strong>without deleting any of your personal source code</strong>.</p><hr /><h2 id="arch-linux-&amp;-cachyos:-bridging-the-plumbing-gap">🔧 Arch Linux &amp; CachyOS: Bridging the Plumbing Gap</h2><p>During our session, we noticed a critical plumbing difference when trying to implement this self-healing loop on a non-SUSE system (like <strong>CachyOS / Arch Linux</strong>):</p><ul><li><strong>The openSUSE Way:</strong> Uses a heavily patched GRUB bootloader maintained by SUSE. When you trigger a rollback, the bootloader dynamically changes which snapshot to boot into.</li><li><strong>The Arch Way:</strong> Most Arch-based systems use modern, minimal bootloaders like <strong>Limine</strong> or standard <strong>systemd-boot</strong>, which hardcode boot arguments (e.g., <code>rootflags=subvol=/@</code>).</li></ul><p>To run SUSE-style rollbacks on Arch/CachyOS without breaking the boot sequence, we use the community tool <strong><code>snapper-rollback</code></strong>.</p><p>Instead of changing bootloader configurations, <code>snapper-rollback</code> renames the Btrfs subvolumes behind the scenes:</p><ol start="1"><li>It renames the broken subvolume (<code>/@</code>) to a backup directory.</li><li>It makes a read-write clone of the selected clean snapshot and names it <code>/@</code>.</li><li>The bootloader boots normally, thinking it&apos;s loading the usual folder, but loads the restored system instead.</li></ol><h2 id="conclusion">🏁 Conclusion</h2><p>The future of systems operations isn&apos;t just about making AI models smarter. It&apos;s about designing architectures where <strong>non-deterministic AI brains are safely sandbox-guarded by deterministic filesystems</strong>. By combining modern MCP integration with legacy partition rollbacks, we get the best of both worlds: autonomous AI operations with a zero-cost undo button.</p></div>]]></content>
  </entry>
  <entry>
    <id>https://blog.nurazhar.com/the-software-janitor-industry-needs-cleaners.html</id>
    <link href="https://blog.nurazhar.com/the-software-janitor-industry-needs-cleaners.html"/>
    <title>The Software Janitor: Why the Industry Needs Fewer Builders and More Cleaners</title>
    <updated>2026-06-14T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><hr /><p>In the technology sector, we are obsessed with the myth of the <strong>&quot;Builder.&quot;</strong></p><p>Job descriptions ask for &quot;makers,&quot; &quot;architects,&quot; and &quot;visionaries.&quot; Companies interview candidates by asking them to spin up new features, write new services, and integrate new frameworks. The industry rewards the act of creation above all else.</p><p>The result of this single-minded obsession? <strong>A massive crisis of digital bloatware.</strong></p><p>Every year, modern software gets slower, heavier, and more fragile. Simple chat clients eat gigabytes of RAM. Basic websites download megabytes of unnecessary JavaScript. Systems are held together by layers of duct tape, undocumented dependencies, and redundant microservices.</p><p>In a world drowning in technical debt, we don&apos;t need more developers who compile generic templates to build <em>new</em> features.</p><p>We need <strong>Software Janitors.</strong></p><hr /><h2 id="what-is-a-software-janitor?">🧹 What is a Software Janitor?</h2><p>A Software Janitor is an engineer who looks at a system and derives satisfaction not from <em>adding</em> code, but from <strong>deleting</strong> it, <strong>optimizing</strong> it, and <strong>cleaning</strong> it up.</p><p>While a builder is busy spinning up a new microservice to handle a simple task, a Software Janitor is:</p><ul><li><strong>Deleting obsolete packages</strong> to reduce the security attack surface (supply chain auditing).</li><li><strong>Refactoring spaghetti code</strong> into simple, readable, and maintainable functions.</li><li><strong>Cleaning up CI/CD pipelines</strong> so they run in 2 minutes instead of 45 minutes.</li><li><strong>Optimizing compilers and flags</strong> (like compiling for <code>x86-64-v3/v4</code> or using Link Time Optimization) to squeeze double the speed out of existing hardware.</li><li><strong>Normalizing database tables</strong> and index structures to resolve write bottlenecks.</li></ul><p>A Software Janitor values <strong>minimalism, execution speed, and mechanical sympathy</strong> with the underlying operating system.</p><hr /><h2 id="the-economics-of-cleaning">🪙 The Economics of Cleaning</h2><p>For a long time, companies treated cleaning as an afterthought. But in 2026, the economics of software development have shifted:</p><ol start="1"><li><strong>The Cloud Bill Crisis</strong>: Running bloated, unoptimized code on millions of servers is incredibly expensive. A performance janitor who optimizes database queries and compiler flags can cut a company&apos;s AWS or GCP hosting bill by 30% to 50% in a week.</li><li><strong>Developer Velocity</strong>: If a developer has to wait 15 minutes for a build to run or navigate through a bloated codebase of dead code, their output drops. Cleaners remove this friction, instantly unlocking the productivity of the entire engineering team.</li><li><strong>Supply Chain Attacks</strong>: With the rise of automated dependency exploits (malicious packages in npm, PyPI, and the AUR), having a minimal dependency tree is a security superpower. Fewer dependencies mean a smaller attack surface.</li></ol><hr /><h2 id="how-to-brand-the-&quot;janitor&quot;-mindset-on-a-resume">💼 How to Brand the &quot;Janitor&quot; Mindset on a Resume</h2><p>If you are a natural &quot;Software Janitor&quot; who hates bloatware and loves clean, fast, minimal systems, you shouldn&apos;t hide it. You should frame it as your greatest strength.</p><p>In the corporate world, &quot;Software Janitor&quot; translates to highly valued, top-paying specializations:</p><ul><li><strong>Site Reliability Engineer (SRE)</strong>: <em>&quot;I keep systems stable, isolated, and resilient by stripping out complexity and configuring deterministic automated boundaries.&quot;</em></li><li><strong>Platform/DevOps Engineer</strong>: <em>&quot;I optimize the developer experience by cleaning up build environments, resolving dependency drift, and designing lightweight, fast-starting runtimes.&quot;</em></li><li><strong>Performance / Systems Architect</strong>: <em>&quot;I rewrite slow components in native, memory-efficient languages (Rust/Go/C++), configure compiler targets for hardware optimizations, and normalize data models.&quot;</em></li></ul><h2 id="the-final-sweep">🏁 The Final Sweep</h2><p>We have enough developers building bloated, slow applications. The next decade belongs to the engineers who know how to sweep away the garbage, automate the mundane, and make systems run fast, safe, and clean.</p><p>So wear the &quot;Software Janitor&quot; badge with pride. Deleting 1,000 lines of redundant code is infinitely more valuable than writing 1,000 lines of new bloatware.</p></div>]]></content>
  </entry>
  <entry>
    <id>https://blog.nurazhar.com/v0-1-set-in-stone-clojure-bitcoin-toolkit.html</id>
    <link href="https://blog.nurazhar.com/v0-1-set-in-stone-clojure-bitcoin-toolkit.html"/>
    <title>Bitcoin v0.1 Set in Stone: Building a functional blockchain toolkit in Clojure</title>
    <updated>2026-06-09T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><hr /><p>Over the last few days, I&apos;ve been deep in the blockchain archives. I wanted to build a portfolio project that would let me study the core engine of peer-to-peer electronic cash without wrestling with an ever-mutating spec sheet.</p><p>I ended up building <strong><code>bsv-clj</code></strong>—a complete Clojure toolkit featuring an idiomatic JSON-RPC client, a read-only wallet tool, and a Ring/Hiccup-powered local block explorer. It connects directly to a <strong>Bitcoin SV (BSV)</strong> node.</p><p>Here is the story of how I designed it, and why Clojure and Bitcoin&apos;s original v0.1 protocol are a perfect match.</p><hr /><h2 id="1.-the-design-philosophy:-locking-the-protocol">1. The Design Philosophy: Locking the Protocol</h2><p>On June 17, 2010, Satoshi Nakamoto wrote a post on the Bitcoin forum that would define a core fork in blockchain history:</p><blockquote><p><em>&quot;The nature of Bitcoin is such that once version 0.1 was released, the core design was set in stone for the rest of its lifetime.&quot;</em></p></blockquote><p>In modern blockchain development, this philosophy is rarely followed. BTC has introduced protocol changes like SegWit and Taproot; other chains rewrite their consensus rules every year. But the Bitcoin SV node client preserves the original v0.1 protocol spec: no arbitrary block size limits, no complex alterations of basic opcodes, and the original UTXO transactional mechanics.</p><p>For an engineer wanting to study the system, this stability is a superpower:</p><ul><li>The <strong>RPC interface</strong> maps directly to Satoshi&apos;s original client design.</li><li>The <strong>transaction schema</strong> and <strong>UTXO model</strong> are clean, minimal, and stable.</li><li>You are querying the foundational data structures that launched the entire industry in 2009.</li></ul><p>With this stable backend in place, my goal was to wrap it in an idiomatic Clojure layer to create the ultimate interactive learning environment.</p><hr /><h2 id="2.-why-clojure-+-bitcoin?">2. Why Clojure + Bitcoin?</h2><p>As I built the toolkit, I realized that Bitcoin&apos;s core mechanics are fundamentally functional. The parallels between Bitcoin&apos;s protocol and Clojure&apos;s standard library are striking:</p><table><thead><tr><th>Bitcoin Protocol Concept</th><th>Clojure Equivalent</th></tr></thead><tbody><tr><td>Transactions are <strong>immutable</strong> once written</td><td>Clojure&apos;s <strong>immutable data structures</strong></td></tr><tr><td>UTXOs are consumed and created, never modified</td><td>Pure functions returning new state with <strong>no side effects</strong></td></tr><tr><td>Script is a <strong>stack-based</strong> language</td><td>Clojure&apos;s <strong>list and sequence abstractions</strong></td></tr><tr><td>Blocks form an <strong>append-only</strong> chain</td><td>Persistent data structures with <strong>structural sharing</strong></td></tr><tr><td>Data-driven API (JSON-RPC)</td><td>Clojure is <strong>data-oriented</strong> by design</td></tr></tbody></table><p>When you query a Bitcoin transaction, you are given a map of inputs and outputs. An input consumes a past output; new outputs declare future UTXOs. There is no <code>balance</code> column in a database. Balance is a derived projection of the unspent UTXO set. In Clojure, this feels like home: you simply filter and reduce a collection of maps.</p><hr /><h2 id="3.-designing-bsv-clj">3. Designing <code>bsv-clj</code></h2><p>The codebase is split into three main modules:</p><h3 id="a.-the-core-rpc-client">A. The Core RPC Client</h3><p>Instead of writing a dynamic helper for each of Bitcoin&apos;s hundred-plus RPC methods, I built a lean HTTP client in <code>bsv.rpc.client</code> that acts as a generic, data-driven wrapper around JSON-RPC 1.0:</p><pre><code class="language-clojure">(defn rpc-call [cfg method params]
  (let [body (json/generate-string {:jsonrpc &quot;1.0&quot;
                                    :id (str (java.util.UUID/randomUUID))
                                    :method method
                                    :params params})
        response (client/post (rpc-url cfg)
                              {:basic-auth [(:user cfg) (:password cfg)]
                               :body body
                               :content-type :json
                               :as :json})]
    (get-in response [:body :result])))
</code></pre><p>This single function handles the authentication, HTTP POST request, JSON encoding/decoding via Cheshire, and error wrapping. On top of this core function, I built high-level modules for <code>blockchain</code>, <code>transaction</code>, <code>network</code>, and <code>mining</code> queries.</p><h3 id="b.-the-wallet-toolkit">B. The Wallet Toolkit</h3><p>The wallet module is read-only for safety. It queries the node for the active UTXO set, tracks balances, and resolves address balances. Rather than relying on heavyweight database indexes, it extracts information directly from the node&apos;s mempool and block databases, showing how Clojure&apos;s sequence processing makes it trivial to filter and group blockchain data:</p><pre><code class="language-clojure">(defn group-by-address [utxos]
  (reduce (fn [acc utxo]
            (let [addr (:address utxo)
                  amount (:amount utxo)]
              (update-in acc [addr] (fnil + 0.0) amount)))
          {}
          utxos))
</code></pre><h3 id="c.-the-hiccup-block-explorer">C. The Hiccup Block Explorer</h3><p>To visualize the blocks and transactions, I built a local web-based explorer using <strong>Ring</strong>, <strong>Compojure</strong>, and <strong>Hiccup</strong>.</p><p>Instead of dealing with a complex JavaScript frontend framework (like React or Vue) or pulling in Tailwind, I used vanilla CSS to style a beautiful, dark-themed dashboard. Hiccup compiles standard Clojure vectors directly into HTML:</p><pre><code class="language-clojure">(defn block-page [block]
  (layout
    (str &quot;Block #&quot; (:height block))
    [:div.container
     [:h1 (str &quot;Block #&quot; (:height block))]
     [:div.card
      [:table
       [:tr [:td &quot;Hash&quot;] [:td (:hash block)]]
       [:tr [:td &quot;Time&quot;] [:td (format-time (:time block))]]
       [:tr [:td &quot;Transactions&quot;] [:td (count (:tx block))]]]]]))
</code></pre><p>The application launches instantly, renders server-side, and runs at 60 FPS without downloading a single megabyte of frontend JS bundles.</p><hr /><h2 id="4.-the-repl-experience">4. The REPL Experience</h2><p>The magic of Clojure is the REPL. When you run <code>clj -A:dev</code>, the REPL starts and loads the <code>user</code> namespace, which exposes immediate development helpers.</p><p>Instead of relying on log statements or debugger breakpoints, you can interact with the blockchain in real-time:</p><pre><code class="language-clojure">;; Connect to local node
(connect! &quot;localhost&quot; 8332 &quot;rpcuser&quot; &quot;rpcpass&quot;)

;; Check node synchronization
(status)
;; =&gt; {:height 850123, :difficulty 1293847291.82, :connections 8}

;; Grab the latest 3 blocks
(latest 3)
;; =&gt; ({:height 850123, :hash &quot;00000...&quot;} ...)

;; Start the local Ring server
(start!)
;; =&gt; &quot;Server started at http://localhost:3000&quot;
</code></pre><p>If you change a route in the explorer or modify the styling, you don&apos;t need to rebuild the project or restart the server. Clojure&apos;s dynamic classloading allows you to re-evaluate the namespace inside your running REPL, and the changes appear instantly on your next page refresh.</p><hr /><h2 id="conclusion">Conclusion</h2><p>Building <code>bsv-clj</code> reinforced my belief that <strong>the data model is the program</strong>.</p><p>By stripping away the bloat of mutable state, heavy frameworks, and shifting protocols, I was able to build a portfolio-quality blockchain toolkit in under 2,500 lines of Clojure. It serves as both a stable toolkit for blockchain protocol research and a demonstration of Clojure&apos;s data-oriented design.</p><p>You can inspect the codebase and try it yourself at: <strong><a href="https://github.com/nurazhardotcom/bsv-clj">github.com/nurazhardotcom/bsv-clj</a></strong>.</p></div>]]></content>
  </entry>
  <entry>
    <id>https://blog.nurazhar.com/native-clojure-desktop-gui-pivot.html</id>
    <link href="https://blog.nurazhar.com/native-clojure-desktop-gui-pivot.html"/>
    <title>The Browser is Fragile: Why I Nuked My Web GUI for Native Clojure Desktop</title>
    <updated>2026-06-08T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><hr /><p>I&apos;ve been building <strong><code>headhunter-agent</code></strong>—a local-first job search assistant and resume compiler—to automate my career strategy.</p><p>Initially, I tried building it as a web app. It was a fragile mess. In under ten minutes of debugging browser errors, I nuked the web files and rewrote the entire thing as a native Clojure desktop app using <strong><code>cljfx</code> (JavaFX)</strong>.</p><p>Here is the unfiltered story of how it went down, and why the browser is the wrong sandbox for local-first tools.</p><hr /><h2 id="1.-the-design:-deep-profiling-&amp;-multi-agent-evaluations">1. The Design: Deep Profiling &amp; Multi-Agent Evaluations</h2><p>The project started as a fork of <code>career-ops</code>, a basic Babashka CLI. But standard resume builders are shallow keyword-stuffers. They produce generic garbage that falls apart in real interviews.</p><p>To fix this, I re-architected it around three core requirements:</p><ul><li><strong>A Data Vault:</strong> An EDN database containing full qualifications, deep work history, and a curated library of <strong>8-12 STAR stories</strong> (Situation, Task, Action, Result).</li><li><strong>A 3-Stage Multi-Agent System (MAS):</strong><ul><li><strong>Agent 1 (FCF Audit):</strong> Auditing the Job Description for legitimacy and Singapore&apos;s Fair Consideration Framework red flags.</li><li><strong>Agent 2 (Fit Analysis):</strong> A brutal comparison of Master Profile gaps/strengths against the JD.</li><li><strong>Agent 3 (Cheat Sheet):</strong> Business model deep dive, tech stack mapping, and cold outreach targets.</li></ul></li><li><strong>Interview Prep:</strong> Cross-referencing the JD with my STAR stories to map out exact answers to the 5 most likely questions.</li></ul><hr /><h2 id="2.-the-browser-fails-(the-10-minute-pivot)">2. The Browser Fails (The 10-Minute Pivot)</h2><p>To build a GUI, I initially went the default route: ClojureScript with Reagent, using <strong>Scittle</strong> to interpret it directly in the browser on GitHub Pages (<code>headhunter.nurazhar.com</code>). I used <code>typst.ts</code> (WASM) to compile PDFs in-browser.</p><p>It was fragile from day one. Web specs evolve, local storage is restrictive, and state-syncing inside a browser sandbox is prone to weird bugs. When <code>headhunter.nurazhar.com</code> failed to load, I didn&apos;t want to waste hours wrestling with browser console errors and JS dependency hell.</p><p>The web stack is bloated. So, we deleted the web code and pivoted.</p><hr /><h2 id="3.-native-desktop-gui-with-cljfx">3. Native Desktop GUI with <code>cljfx</code></h2><p>Instead of the browser, I shifted to <strong><code>cljfx</code></strong>, a declarative wrapper for JavaFX.</p><p>Now, it is a standalone desktop application. No local web servers, no browser tabs, no JavaScript. It runs directly on the JVM, rendering native desktop windows on Arch/CachyOS.</p><p>Why this actually works:</p><ol start="1"><li><strong>Zero Browser Bloat:</strong> It is a compiled program, not a tab running in an engine.</li><li><strong>Immutable UI State:</strong> The entire app state is inside a single Clojure atom. The UI is a pure function of the data. If the data is correct, the UI literally cannot render bugs.</li><li><strong>No Thread Freezing:</strong> Heavy Gemini API calls run in Clojure <code>futures</code> (background threads), while <code>Platform/runLater</code> dispatches the results back to the main UI. The desktop app stays responsive at 60 FPS while the agents run.</li><li><strong>Local Privacy:</strong> Everything is saved as local <code>.edn</code> files. No network syncing, no cloud databases, no trackers.</li></ol><hr /><h2 id="conclusion">Conclusion</h2><p>We are conditioned to default to the web for everything. But for local-first utilities, the browser is just a bloated, insecure sandbox that gets in the way.</p><p>Writing a native desktop app in Clojure is faster, runs better, and is vastly more stable. If your local tools are acting up because of browser quirks, stop trying to fix the JS bundle. Just delete it.</p><p><em>Codebase:</em> <strong><a href="https://github.com/nurazhardotcom/headhunter-agent">headhunter-agent</a></strong></p></div>]]></content>
  </entry>
  <entry>
    <id>https://blog.nurazhar.com/first-post.html</id>
    <link href="https://blog.nurazhar.com/first-post.html"/>
    <title>Hello Clojure!</title>
    <updated>2026-05-31T23:59:59+00:00</updated>
    <content type="html"><![CDATA[<div><hr /><p>Welcome to my new digital garden dedicated to <strong>Clojure, Babashka, Lisp, and developer workflow automation</strong>.</p><p>I built this blog using <strong><a href="https://github.com/borkdude/quickblog">Quickblog</a></strong>—a lightweight static site generator powered by <strong><a href="https://babashka.org/">Babashka</a></strong>. It allows me to write posts in plain Markdown and compile them into lightning-fast, static HTML with zero overhead.</p><h2 id="why-clojure-&amp;-babashka?">Why Clojure &amp; Babashka?</h2><p>In an engineering landscape increasingly dominated by complex build pipelines and massive toolchains, <strong>Clojure</strong> offers a breath of fresh air:</p><ul><li><strong>REPL-Driven Development:</strong> Immediate feedback and interactive coding.</li><li><strong>Data as Code:</strong> Structural elegance and powerful macro systems.</li><li><strong>Babashka:</strong> Brings Clojure&apos;s expressive power to shell scripting, replacing heavy bash scripts with fast-starting, native Clojure binaries.</li></ul><p>Over the coming weeks, I will be sharing my automation scripts, system configurations, and architectural musings as I continue to design my ultimate developer workflow.</p><p>Stay tuned!</p></div>]]></content>
  </entry>
</feed>
