Thoughts on clojure, linux, security and Bitcoin's 0.1v (Satoshi Nakamoto's Design) in an era of artificial intelligence
If you use autonomous coding agents for more than a few hours, you will hit a wall.
It is a silent, exhausting kind of fatigue. You didnโt write a single line of boilerplate, you didn't fight compiler configurations, and you didn't search documentation. Yet, your head is throbbing, you feel dizzy, and your mental stamina is completely shot.
This is Cognitive Vertigo (or Cognitive G-Force). It is caused by the collapse of latency between intent and execution. 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.
To survive a full-time role in this new era without burning out, you must discard the traditional developer workflow and adopt an AI-First Software Development Lifecycle (SDLC).
In the traditional SDLC, developers spend 80% of their energy coding and 20% designing. When coding, your brain operates in a slow, sequential state.
In an AI-First SDLC, this relationship is inverted:
gantt
title Traditional vs. AI-First Developer Lifecycles
dateFormat X
axisFormat %s
section Traditional SDLC (Active Typing)
Design (Slow) :active, des1, 0, 20
Coding & Debugging (Grind) :active, cod1, 20, 100
section AI-First SDLC (Agent Orchestration)
Spec & Architecture (80% Brain) :crit, des2, 0, 80
Agent Execution & Tests (20% Verify) :crit, cod2, 80, 100
By separating the Design Phase (Think Slow) from the Execution Phase (Build Fast), you shield your brain from the constant feedback loops that drain your mental battery.
Drawing inspiration from structured frameworks like SteveGJones/ai-first-sdlc-practices, here is the blueprint to structure your day-to-day workflow for maximum stamina:
graph TD
A[1. Think Slow: Design & Spec] -->|Define boundaries, schemas, & tests| B[2. The Constitution: Rules of Play]
B -->|Hand over blueprint to Agent| C[3. Build Fast: Async Execution]
C -->|Zero-Technical-Debt Pipeline| D[4. Verify: Black-Box Validation]
D -->|If tests fail| C
D -->|If tests pass| E[5. Ship & Cooldown]
Do not explain your design philosophy to an agent in every chat. Define a permanent configuration file (like a CONSTITUTION.md or .clauderc) in your repository root. This constitution outlines:
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.
validate --syntax/--quick/--pre-push)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:
validate --syntax: Evaluates basic file structures and imports immediately after generation.validate --quick: Runs fast unit tests and linter audits before commits are written.validate --pre-push: Executes the full integration test suite and scans for security anomalies.If the validation script passes, trust the implementation. Focus your energy on verifying the boundaries, not inspecting the lines.
Pair-programming with an agent in real-time is a cognitive trap. Instead, delegate asynchronously:
task.md or implementation_plan.md).Bookmark this checklist and use it as your daily operating system:
| Habit | How to Implement | Why it Saves Your Brain |
|---|---|---|
| Zero-Cache Brain | Write all active tasks in a physical notepad or task.md. Never hold stack frames in your head. | Frees up working memory. |
| Test-Driven Intent | Write the tests or output schemas first. Let the agent code until the tests pass. | Eliminates the need for line-by-line review. |
| Paced Breaks | Set a timer to step away from the IDE for 5 minutes after every major agent execution. | Prevents cognitive overheating. |
| Interface Design | Spend your time writing HoneySQL configurations, Malli schemas, or API signatures. | Keeps your brain in the "System Architect" tier. |
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.
Published: 2026-06-14
Tagged: architecture automation workflow developer-experience
Lisp (specifically Clojure) and Bitcoin are a natural match. Both prioritize immutable data structures, declarative transformations, and minimal state side-effects.
In this post, we'll walk through the architectural blueprint of bsv-clj, 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.
The first layer is talking to the Bitcoin node itself over JSON-RPC. Using http-kit and cheshire (JSON parser), we can call remote node methods natively:
(def rpc-config
{:url "http://127.0.0.1:8332"
:user "rpcuser"
:password "rpcpassword"})
(defn rpc-call
"Executes a JSON-RPC method call on the Bitcoin node."
([method] (rpc-call method []))
([method params]
(let [payload (json/generate-string
{:jsonrpc "2.0"
:id "clj-rpc"
:method method
:params params})
options {:headers {"Content-Type" "application/json"}
: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 "HTTP Error: " (:status response))}))))
With this, running (rpc-call "getblockchaininfo") returns a native Clojure map representing the blockchain status.
Instead of querying third-party APIs (which exposes your financial privacy), the sovereign way is to read directly from your local wallet database.
Using next.jdbc and honey.sql, we query spendable outputs (UTXOs) from a local wallet sandbox:
(defn query-wallet-utxos
"Queries local SQLite database to list spendable outputs."
[db-path]
(let [ds (jdbc/get-datasource {:dbtype "sqlite" :dbname db-path})
query (sql/format {:select [:txid :outputIndex :amount :state]
:from [:outputs]
:where [:= :state "SPENDABLE"]})]
(jdbc/execute! ds query)))
To prevent malformed transaction request structures from reaching signing nodes, we define a declarative schema using Malli:
(def TransactionRequestSchema
[:map
[:appId :string]
[:inputs [:vector [:map
[:txid [:re #"^[a-fA-F0-9]{64}$"]]
[:outputIndex :int]]]]
[:outputs [:vector [:map
[:toAddress [:re #"^[13m-n2-9][a-km-zA-HJ-NP-Z1-9]{26,34}$"]]
[:satoshis [:pos-int]]]]]
[:metadata [:map
[:timestamp :int]
[:data :string]]]])
This enforces strict hexagonal boundaries: matching valid transaction hex patterns, valid Bitcoin addresses, positive satoshi spends, and clean metadata.
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:
(def sg-nric-regex #"(?i)\b[SGTFAM]\d{7}[A-Z]\b")
(defn redact-payload-metadata
"Scans metadata text and redacts sensitive Singapore NRIC profiles."
[text]
(clojure.string/replace text sg-nric-regex "[REDACTED_NRIC]"))
By putting the RPC clients, local SQLite DBs, and payload validation under a Clojure workflow, we achieve:
You can check out the sandbox project source code on GitHub: github.com/nurazhardotcom/bsv-clj.
Published: 2026-06-14
Tagged: architecture security clojure bitcoin sandbox
A few minutes ago, I experienced a strange kind of development vertigo.
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 46 minutes, we completed 8 distinct, multi-system tasks.
I literally told the agent: "I'm experiencing G-force and feel like vomiting. It's too fast."
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.
Here is the exact timeline of what was accomplished between 03:26 AM and 04:12 AM this morning:
clojure.nurazhar.com to blog.nurazhar.com, mapping custom CNAMEs and updating DNS configurations.bsv-clj), analyzed its Malli schemas, SQLite wallet querying, and DLP regex layers, and published a technical integration post about it.Before agentic IDE tools, this workload would have taken half a day to a weekend of focused manual effort.
The time wouldn't be spent thinking; it would be consumed by the friction of execution:
When an agent handles the execution, you become the Director. You are no longer writing the code; you are compiling intent.
But this speed comes with a cost: Cognitive G-force. When you do not have to wait for compilers to run, files to save, or documentation to write, the latency between designing a system and seeing it live 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.
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.
How are you managing the cognitive load as developer velocity accelerates? Let's discuss.
Published: 2026-06-14
Tagged: automation workflow developer-experience reflection systems
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 PKGBUILDs, the AUR, AUR helpers (like paru/yay), and custom repository forks can quickly become confusing.
If you have ever wondered:
This guide breaks it down with clear diagrams, comparisons, and technical details.
To understand this system, let's look at a quick analogy:
.pkg.tar.zst file). This is what you actually consume (install).paru) is your automated personal chef. It visits the recipe site, fetches the instructions, downloads the ingredients, cooks it, and serves it to you.Here is how source code on the internet eventually becomes a running application on your machine:
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["๐ Upstream Source Code<br/>(GitHub, GitLab, tar.gz)"]:::source
subgraph AUR_Directory ["๐ Arch User Repository (AUR)"]
B["๐ PKGBUILD Script<br/>(Text instructions)"]:::recipe
end
C["๐ ๏ธ AUR Helper (paru / yay)"]:::build
D["โ๏ธ makepkg Tool<br/>(Local Compilation)"]:::build
E["๐ฆ Built Arch Package<br/>(*.pkg.tar.zst)"]:::build
F["๐ฅ๏ธ Target System<br/>(Installed via pacman)"]:::final
%% Flow
B -->|1. paru pulls recipe| C
A -->|2. Downloads code| C
C -->|3. Feeds code & recipe| D
D -->|4. Compiles & packages| E
E -->|5. Installs binary| F
A PKGBUILD is a plain-text shell script containing variables and functions (like prepare(), build(), and package()).
Here is a simplified look at what a PKGBUILD does:
pkgname=freebuff-bin
pkgver=0.0.106
arch=('x86_64')
depends=('glibc')
source=("https://github.com/CodebuffAI/codebuff/releases/download/v${pkgver}/freebuff-linux")
package() {
# Install the precompiled binary into the system binary path
install -Dm755 "${srcdir}/freebuff-linux" "${pkgdir}/usr/bin/freebuff"
}
No. The AUR website only hosts text-based PKGBUILD scripts (along with occasional helper configuration files). It does not store .exe, .bin, or .pkg.tar.zst packages.
However, some package names on the AUR end with -bin (e.g., freebuff-bin). This is a naming convention:
freebuff (Source package): 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.freebuff-bin (Binary package): 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.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 CachyOS-PKGBUILDS.
They do this to implement three key enhancements:
x86-64 CPUs (compatible with older computers). CachyOS rebuilds these packages targeting x86-64-v3 (which requires AVX2) and x86-64-v4 (AVX-512). This makes CPU-bound software significantly faster.linux-cachyos), CPU scheduler configurations (scx for sched-ext), and custom compiler flags (-O3, LTO).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:
.deb).deb files) hosted in large central repositories..deb package requires a complex file structure (including a debian/control file, debian/rules file, etc.) and utilities like dpkg-buildpackage. It is significantly more difficult to write than a single PKGBUILD script..rpm).rpm files)..rpm package requires writing an RPM .spec file. While structured, spec files are notoriously verbose and complex compared to the clean, Bash-like syntax of PKGBUILDs.PKGBUILD is a single, readable Bash script. Debian and Red Hat packages require complex build directories and specialized spec/rule syntaxes.x86-64 hardware. CachyOS rebuilds everything specifically to leverage modern CPU instructions.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 ["Arch Linux (AUR) Model"]
A1["๐ AUR Server<br/>(Hosts PKGBUILD Script)"]:::arch
A2["๐ Upstream Source Code<br/>(GitHub / Tarball)"]:::arch
A3["โ๏ธ User's Machine<br/>(Compiles & Packages)"]:::compile
A4["๐ฅ๏ธ Installed Program"]:::system
A1 -->|1. Downloads script| A3
A2 -->|2. Downloads code| A3
A3 -->|3. Install package| A4
end
subgraph Debian_RedHat_Model ["Debian (PPA) / RedHat (COPR) Model"]
B1["๐ Recipe / Spec File"]:::debian
B2["๐ Upstream Source Code"]:::debian
B3["โ๏ธ Build Servers<br/>(Canonical/RedHat Cloud)"]:::compile
B4["๐ฆ Hosted Binary Repository<br/>(.deb / .rpm)"]:::debian
B5["โ๏ธ User's Machine<br/>(Apt / DNF Download)"]:::compile
B6["๐ฅ๏ธ Installed Program"]:::system
B1 -->|1. Build trigger| B3
B2 -->|2. Upload code| B3
B3 -->|3. Compile & store| B4
B4 -->|4. Download binary| B5
B5 -->|5. Install| B6
end
| Feature | Upstream Code / Release | PKGBUILD Script | AUR (Arch User Repo) | CachyOS PKGBUILD Repo |
|---|---|---|---|---|
| What is it? | Raw application source code or generic binary releases. | A text file containing build instructions. | A community directory of PKGBUILD scripts. | A curated collection of CachyOS-specific PKGBUILDs. |
| Host Location | GitHub, GitLab, official websites. | Local filesystem (e.g., /tmp). | aur.archlinux.org | github.com/CachyOS/CachyOS-PKGBUILDS |
| Content Type | .tar.gz, .zip, .rs, .cpp, .go | Plain text shell script (bash syntax). | Git repositories containing only PKGBUILD files. | Git repository containing CachyOS package build scripts. |
| Compilation | N/A (unbuilt source). | Happens when you run makepkg on it. | User compiles it locally (unless it's a -bin package). | Pre-compiled on CachyOS servers; distributed via pacman mirrors. |
| Optimizations | Generic. | Up to the user's local configuration. | Generic. | Highly optimized (AVX2, AVX-512, -O3, LTO). |
When you want to run new software like Freebuff (a terminal-based AI coding assistant):
CachyOS-PKGBUILDS.paru (e.g., paru -S freebuff-bin).paru reads the recipe from the AUR, downloads the binary release from upstream, and installs it cleanly on your system.Published: 2026-06-14
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.
Here is how to optimize your developer workflow in Antigravity IDE using Gemini's Thinking Budget configurations.
Modern models (like Gemini 2.5/3.5 Flash and Pro) introduce a configurable Thinking Level (or Reasoning Budget). This setting represents how many internal, step-by-step reasoning tokens the model compiles before writing the final code.
To manage your tokens, you need to understand when to toggle between them:
| Setting | When to Use | Clojure Dev Tasks | Quota Consumption | Latency |
|---|---|---|---|---|
| Low / Off | Simple, rote coding and direct translations | Boilerplate templating, converting JSON to edn, writing basic Babashka shell scripts. | Minimal (Input + Output only) | Very Fast |
| Medium | Logical, multi-step coding problems | Standard app functions, writing test suites, refactoring standard functions. | Moderate | Moderate |
| High | Highly complex, stateful, or abstract logic | Designing advanced macros, debugging race conditions in core.async pipelines, orchestrating multi-agent systems. | Maximum (Adds hidden thinking tokens) | Slower |
Instead of copy-pasting your entire core.clj namespace to fix an error:
clojure.spec).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 2,000 thinking tokens, those tokens are sent back as input on the next turn.
If you write automation scripts that query the Gemini API directly, you can configure the thinking budget dynamically in your payload using generationConfig.
Here is a Babashka script template showing how to switch thinking budgets dynamically:
#!/usr/bin/env bb
(require '[babashka.http-client :as http]
'[cheshire.core :as json])
(def api-key (System/getenv "GEMINI_API_KEY"))
(defn query-gemini [prompt thinking-budget]
;; thinking-budget = 0 (Minimal/Low)
;; thinking-budget = 1024 or 2048 (Medium/High reasoning token limit)
(let [url (str "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=" api-key)
payload {:contents [{:parts [{:text prompt}]}]
:generationConfig
(merge
{:temperature 1.0}
(if (zero? thinking-budget)
{:thinkingConfig {:thinkingBudget 0}}
{:thinkingConfig {:thinkingBudget thinking-budget}}))}]
(-> (http/post url {:headers {"Content-Type" "application/json"}
: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 "Generate a basic Babashka task definition for file-syncing." 0))
Treating LLM usage like hardware memory management makes you a highly efficient developer:
Published: 2026-06-14
Tagged: clojure llm workflow developer-experience babashka
When migrating articles from other static site generators (like Hugo, Jekyll, or standard GitHub-flavored Markdown) to Quickblog, you might run into a jarring visual bug: your post titles show up twice at the top of the page.
Here is why it happens, and how to programmatically fix it across your entire codebase.
In standard Markdown styling, it is natural to start your file with an H1 header matching your title:
Title: My Great Post
Date: 2026-06-14
---
# My Great Post
This is the content of my post...
However, Quickblog uses a rendering template (typically found in templates/post.html) to dynamically construct your posts. If you inspect the default template, you will see it already wraps your metadata {{title}} in an H1 block:
<h1>
{% if post-link %}<a href="{{post-link}}">{% endif %}
{{title}}
{% if post-link %}</a>{% endif %}
</h1>
{{body | safe}}
Because Quickblog inserts the title dynamically, any # Title header you manually write inside the Markdown body will result in a double title on the compiled HTML page.
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.
Run this command in your repository root:
python3 -c '
import glob, re
for f in glob.glob("posts/*.md"):
with open(f, "r") as file:
content = file.read()
# Remove the first H1 header line and any trailing empty lines immediately after it
new_content = re.sub(r"(?m)^# .*\n\n?", "", content, count=1)
with open(f, "w") as file:
file.write(new_content)
'
This script:
posts/ folder.(?m)^# .*\n\n?) to find only the first occurrence of an H1 header (# ) at the beginning of a line.Once completed, run bb quickblog render to rebuild the pages, and the layout will render perfectly with a single title block.
Published: 2026-06-14
Tagged: simplicity automation clojure web-development
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.
For a blog, static HTML is the ultimate architecture. This blog is powered by Quickblog, a lightweight static site generator designed for the Clojure/Babashka ecosystem by Michiel Borkent (@borkdude).
Here is a breakdown of how it works under the hood and why this architecture is a superior design choice.
At the heart of Quickblog is Babashka, a fast-starting scripting runtime for Clojure.
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:
Quickblog is executed entirely as a Babashka task defined in bb.edn.
Quickblog's architecture follows a clean, functional pipeline:
graph TD
A[Markdown Posts /posts] --> B(Babashka CLI / bb.edn)
C[HTML Templates /templates] --> B
D[Assets /assets] --> B
B -->|bb quickblog render| E[Pure HTML/CSS/RSS /public]
E -->|Git Push| F[GitHub Pages]
F -->|HTTPS CNAME| G(blog.nurazhar.com)
/posts directory and reads configuration parameters from bb.edn./templates/base.html, /templates/post.html, etc.).index.html (the homepage loading up to N posts).archive.html (a list of all posts).tags.html (filtered views).feed.xml (RSS feed)./public directory, ready to be hosted anywhere.In his original 2008 Bitcoin v0.1 release, Satoshi Nakamoto focused on peer-to-peer simplicityโdesigning a system with direct nodes, no unnecessary intermediate layers, and raw efficiency.
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: clean, readable, and immortal HTML.
Published: 2026-06-14
Tagged: simplicity architecture clojure babashka
In enterprise environments (like Singapore's defense and corporate sectors), Vulnerability Assessment and Penetration Testing (VAPT) 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.
It is a high-overhead, low-velocity cycle.
But when you build using a "No-Backend" architectureโ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 less than 15 minutes.
Here is how we did exactly that for lagu-lagu (a real-time Singapore PayNow payout registry for independent artists).
Our application uses a zero-server runtime model:
By deleting standard backend servers (like Spring Boot or Express), we deleted 99% of our dependency overhead. We don't have server routes to exploit, memory leaks to exploit, or unpatched framework packages.
But minimalist doesn't mean bulletproof. We audited the system and found three real vulnerabilities.
Our GCP function was querying the database and generating HTML string templates dynamically to serve back to the HTMX frontend:
const html = rows.map(artist => `<h3>${artist.name}</h3>...`).join("");
<script>steal(document.cookie)</script>), Postgres would store it, the GCP function would fetch it, and the browser would execute it.function escapeHtml(unsafe) {
if (!unsafe) return '';
return unsafe.toString()
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
The split trigger calculated the 80/20 division using standard multiplication (NEW.amount * 0.80). But the schema did not explicitly validate that payment amounts were positive.
SGD -10.00) bypassed frontend validation or was injected via a rogue webhook, the database would generate a negative artist payout (SGD -8.00), causing financial ledger logic corruption.ALTER TABLE transactions ADD CONSTRAINT check_positive_amount CHECK (amount > 0);
ALTER TABLE payouts ADD CONSTRAINT check_positive_payout CHECK (amount_sent > 0);
In our try-catch block, we returned raw stack errors to the client:
catch (error) {
return res.status(500).json({ error: error.message });
}
catch (error) {
console.error("Internal API error:", error);
return res.status(500).json({ error: "Internal Server Error" });
}
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:
echo '{"security_and_analysis":{"secret_scanning":{"status":"enabled"},"secret_scanning_push_protection":{"status":"enabled"}}}' \
| gh api --method PATCH repos/nurazhardotcom/lagu-lagu --input -
With Push Protection 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.
Modern security compliance (like Singapore's Cyber Trust Mark or corporate checklists) tends to focus on administrative processes, PDF paperwork, and constant scanning.
But complexity is the ultimate enemy of security.
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.
Published: 2026-06-14
As of mid-2026, the tech industry has reached a quiet consensus: AI agents cannot safely manage servers using raw bash terminal access.
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.
So how do we give AI agents the keys to the kingdom without risking total disaster? We build a legacy safety net.
This post explores the concepts from our deep dive into the openSUSE Autonomous OS pathwayโspecifically, how openSUSE is pairing futuristic AI standard protocols with battle-tested filesystem technologies to build closed-loop self-healing systems.
To understand how AI interacts with an operating system today, we can break the architecture down into four layers using a simple Car, Steering Wheel, and Driver analogy:
graph TD
A["๐ง 1. The App / Client (The Driver) <br>Cursor, Claude, Antigravity Client"] -->|Sends MCP commands| B["๐ 2. Model Context Protocol (The Steering Wheel) <br>Standardized Tool-Calling Protocol"]
B -->|Translates to local shell| C["โ๏ธ 3. The MCP Server (The ECU / Hands) <br>Local python/node daemon translating commands"]
C -->|Executes filesystem changes| D["๐๏ธ 4. The Operating System (The Engine) <br>openSUSE, Btrfs, Snapper, Hardware"]
In 2026, different enterprise Linux vendors are approaching AI SysOps from unique angles:
| Distribution | Core Strategy | Key Technologies | Ideal Workloads |
|---|---|---|---|
| openSUSE / SUSE | The Self-Healing Loop | Btrfs + Snapper + transactional-update | Autonomous mitigation, self-updating servers, write-capable AI agents. |
| Red Hat / RHEL | Telemetry & Diagnostics | linux-mcp-server + systemd logs | Compliance-heavy environments, read-only AI audits, remote troubleshooting. |
| Amazon Linux | Cloud Scaling | AWS Agent Toolkit + Amazon Q | Cloud-native microservices, ECS clustering, automated infrastructure scaling. |
| Canonical / Ubuntu | Sovereignty & Local AI | Local LLMs + Snap/Flatpak sandboxes | Offline developer setups, on-device training, high-privacy data processing. |
SUSE's key advantage is that they didn't write new, experimental code to protect systems from AI hallucinations. Instead, they leveraged their decade-old, bulletproof filesystem technology: Btrfs and Snapper.
This creates the Self-Healing Loop:
[ 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.
If the AI makes a destructive change, the entire OS is rolled back to the clean snapshot instantaneously. Because /home is typically kept on a separate subvolume/partition, the system is restored to safety without deleting any of your personal source code.
During our session, we noticed a critical plumbing difference when trying to implement this self-healing loop on a non-SUSE system (like CachyOS / Arch Linux):
rootflags=subvol=/@).To run SUSE-style rollbacks on Arch/CachyOS without breaking the boot sequence, we use the community tool snapper-rollback.
Instead of changing bootloader configurations, snapper-rollback renames the Btrfs subvolumes behind the scenes:
/@) to a backup directory./@.The future of systems operations isn't just about making AI models smarter. It's about designing architectures where non-deterministic AI brains are safely sandbox-guarded by deterministic filesystems. 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.
Published: 2026-06-14
In the technology sector, we are obsessed with the myth of the "Builder."
Job descriptions ask for "makers," "architects," and "visionaries." 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.
The result of this single-minded obsession? A massive crisis of digital bloatware.
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.
In a world drowning in technical debt, we don't need more developers who compile generic templates to build new features.
We need Software Janitors.
A Software Janitor is an engineer who looks at a system and derives satisfaction not from adding code, but from deleting it, optimizing it, and cleaning it up.
While a builder is busy spinning up a new microservice to handle a simple task, a Software Janitor is:
x86-64-v3/v4 or using Link Time Optimization) to squeeze double the speed out of existing hardware.A Software Janitor values minimalism, execution speed, and mechanical sympathy with the underlying operating system.
For a long time, companies treated cleaning as an afterthought. But in 2026, the economics of software development have shifted:
If you are a natural "Software Janitor" who hates bloatware and loves clean, fast, minimal systems, you shouldn't hide it. You should frame it as your greatest strength.
In the corporate world, "Software Janitor" translates to highly valued, top-paying specializations:
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.
So wear the "Software Janitor" badge with pride. Deleting 1,000 lines of redundant code is infinitely more valuable than writing 1,000 lines of new bloatware.
Published: 2026-06-14
Tagged: simplicity reflection career-ops systems
Over the last few days, I'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.
I ended up building bsv-cljโ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 Bitcoin SV (BSV) node.
Here is the story of how I designed it, and why Clojure and Bitcoin's original v0.1 protocol are a perfect match.
On June 17, 2010, Satoshi Nakamoto wrote a post on the Bitcoin forum that would define a core fork in blockchain history:
"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."
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.
For an engineer wanting to study the system, this stability is a superpower:
With this stable backend in place, my goal was to wrap it in an idiomatic Clojure layer to create the ultimate interactive learning environment.
As I built the toolkit, I realized that Bitcoin's core mechanics are fundamentally functional. The parallels between Bitcoin's protocol and Clojure's standard library are striking:
| Bitcoin Protocol Concept | Clojure Equivalent |
|---|---|
| Transactions are immutable once written | Clojure's immutable data structures |
| UTXOs are consumed and created, never modified | Pure functions returning new state with no side effects |
| Script is a stack-based language | Clojure's list and sequence abstractions |
| Blocks form an append-only chain | Persistent data structures with structural sharing |
| Data-driven API (JSON-RPC) | Clojure is data-oriented by design |
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 balance 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.
bsv-cljThe codebase is split into three main modules:
Instead of writing a dynamic helper for each of Bitcoin's hundred-plus RPC methods, I built a lean HTTP client in bsv.rpc.client that acts as a generic, data-driven wrapper around JSON-RPC 1.0:
(defn rpc-call [cfg method params]
(let [body (json/generate-string {:jsonrpc "1.0"
: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])))
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 blockchain, transaction, network, and mining queries.
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's mempool and block databases, showing how Clojure's sequence processing makes it trivial to filter and group blockchain data:
(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))
To visualize the blocks and transactions, I built a local web-based explorer using Ring, Compojure, and Hiccup.
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:
(defn block-page [block]
(layout
(str "Block #" (:height block))
[:div.container
[:h1 (str "Block #" (:height block))]
[:div.card
[:table
[:tr [:td "Hash"] [:td (:hash block)]]
[:tr [:td "Time"] [:td (format-time (:time block))]]
[:tr [:td "Transactions"] [:td (count (:tx block))]]]]]))
The application launches instantly, renders server-side, and runs at 60 FPS without downloading a single megabyte of frontend JS bundles.
The magic of Clojure is the REPL. When you run clj -A:dev, the REPL starts and loads the user namespace, which exposes immediate development helpers.
Instead of relying on log statements or debugger breakpoints, you can interact with the blockchain in real-time:
;; Connect to local node
(connect! "localhost" 8332 "rpcuser" "rpcpass")
;; Check node synchronization
(status)
;; => {:height 850123, :difficulty 1293847291.82, :connections 8}
;; Grab the latest 3 blocks
(latest 3)
;; => ({:height 850123, :hash "00000..."} ...)
;; Start the local Ring server
(start!)
;; => "Server started at http://localhost:3000"
If you change a route in the explorer or modify the styling, you don't need to rebuild the project or restart the server. Clojure's dynamic classloading allows you to re-evaluate the namespace inside your running REPL, and the changes appear instantly on your next page refresh.
Building bsv-clj reinforced my belief that the data model is the program.
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's data-oriented design.
You can inspect the codebase and try it yourself at: github.com/nurazhardotcom/bsv-clj.
Published: 2026-06-09
Tagged: clojure blockchain functional-programming repl web-development bitcoin
I've been building headhunter-agentโa local-first job search assistant and resume compilerโto automate my career strategy.
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 cljfx (JavaFX).
Here is the unfiltered story of how it went down, and why the browser is the wrong sandbox for local-first tools.
The project started as a fork of career-ops, a basic Babashka CLI. But standard resume builders are shallow keyword-stuffers. They produce generic garbage that falls apart in real interviews.
To fix this, I re-architected it around three core requirements:
To build a GUI, I initially went the default route: ClojureScript with Reagent, using Scittle to interpret it directly in the browser on GitHub Pages (headhunter.nurazhar.com). I used typst.ts (WASM) to compile PDFs in-browser.
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 headhunter.nurazhar.com failed to load, I didn't want to waste hours wrestling with browser console errors and JS dependency hell.
The web stack is bloated. So, we deleted the web code and pivoted.
cljfxInstead of the browser, I shifted to cljfx, a declarative wrapper for JavaFX.
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.
Why this actually works:
futures (background threads), while Platform/runLater dispatches the results back to the main UI. The desktop app stays responsive at 60 FPS while the agents run..edn files. No network syncing, no cloud databases, no trackers.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.
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.
Codebase: headhunter-agent
Published: 2026-06-08
Tagged: automation clojure multi-agent-system cljfx career-ops desktop-gui
Welcome to my new digital garden dedicated to Clojure, Babashka, Lisp, and developer workflow automation.
I built this blog using Quickblogโa lightweight static site generator powered by Babashka. It allows me to write posts in plain Markdown and compile them into lightning-fast, static HTML with zero overhead.
In an engineering landscape increasingly dominated by complex build pipelines and massive toolchains, Clojure offers a breath of fresh air:
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.
Stay tuned!
Published: 2026-05-31
Tagged: automation clojure babashka