<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Coderain]]></title><description><![CDATA[freewriting experiences of an developer ]]></description><link>https://tech.sedhu.me</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1623951099497/XMx13Xkg_.png</url><title>Coderain</title><link>https://tech.sedhu.me</link></image><generator>RSS for Node</generator><lastBuildDate>Sun, 10 May 2026 05:19:26 GMT</lastBuildDate><atom:link href="https://tech.sedhu.me/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The Layered Cake of Claude Code]]></title><description><![CDATA[A practical, deep-dive map of Claude Code — what every folder, file, plugin, skill, hook, agent, and MCP actually does, how to compose them, how to swap the brain for OpenRouter or Ollama, and how it ]]></description><link>https://tech.sedhu.me/the-layered-cake-of-claude-code</link><guid isPermaLink="true">https://tech.sedhu.me/the-layered-cake-of-claude-code</guid><dc:creator><![CDATA[Sedhu]]></dc:creator><pubDate>Sun, 03 May 2026 17:02:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/5ffd78174084696c9d22a46f/4a42f317-36ef-4ec4-99ee-1d50dcf835db.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>A practical, deep-dive map of Claude Code — what every folder, file, plugin, skill, hook, agent, and MCP actually does, how to compose them, how to swap the brain for OpenRouter or Ollama, and how it stacks up against Mastra and Pi.</p>
</blockquote>
<blockquote>
<p><em>Note: in this post, "Claude Code" refers to Anthropic's terminal coding harness. I'll use "harness" a lot — it's the cockpit that wraps the LLM and gives it tools, memory, and a runtime.</em></p>
</blockquote>
<hr />
<h2>Why think in layers?</h2>
<p>Picture Claude Code as a <strong>layered cake</strong> — or, if you prefer, an apartment building.</p>
<ul>
<li><p>The <strong>LLM is the foundation slab</strong> — raw intelligence with no plumbing.</p>
</li>
<li><p>The <strong>harness is the building</strong> — walls, wiring, elevators, fire alarms.</p>
</li>
<li><p>Each <strong>folder layer</strong> is a floor with its own residents (config, memory, plugins).</p>
</li>
<li><p><strong>Skills, subagents, MCPs, and hooks</strong> are the appliances and staff that actually get work done on each floor.</p>
</li>
</ul>
<p>When something behaves weirdly — Claude forgets context, burns tokens, picks the wrong tool — it's almost always because you don't yet have a clear mental model of <em>which floor the bug is on</em>. The point of this post is to give you that floor plan.</p>
<p><strong>Diagram 1 — The layered stack (top = most specific, bottom = rawest):</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/5ffd78174084696c9d22a46f/ac85adf0-8528-4712-94e4-a5fbbb420953.png" alt="" style="display:block;margin:0 auto" />

<hr />
<h2>The full stack at a glance</h2>
<p>From bottom (rawest) to top (most project-specific):</p>
<ul>
<li><p><strong>Layer 0 — The LLM.</strong> Claude Sonnet, Opus, Haiku, or any model you route to.</p>
</li>
<li><p><strong>Layer 1 — The Harness.</strong> Claude Code itself — the loop that takes your prompt, decides on tool calls, edits files, runs bash.</p>
</li>
<li><p><strong>Layer 2 —</strong> <code>~/.claude/</code> <strong>(home).</strong> Your <em>global</em> settings, plugins, memory, and agents — applies to every project on this machine.</p>
</li>
<li><p><strong>Layer 3 —</strong> <code>.claude/</code> <strong>(project).</strong> Repo-level config that travels with the codebase and is shared with your team.</p>
</li>
<li><p><strong>Layer 4 — Memory files.</strong> <code>CLAUDE.md</code> (committed) and <code>CLAUDE.local.md</code> (gitignored, personal).</p>
</li>
<li><p><strong>Layer 5 — Plugins.</strong> Installable bundles of skills, agents, hooks, commands, and MCP servers.</p>
</li>
<li><p><strong>Layer 6 — Skills.</strong> <code>SKILL.md</code> files that teach Claude how to do a specific task well.</p>
</li>
<li><p><strong>Layer 7 — Subagents.</strong> Specialized Claudes you dispatch with their own context window.</p>
</li>
<li><p><strong>Layer 8 — Hooks.</strong> Middleware that fires on lifecycle events (PreToolUse, PostToolUse, SessionStart, …).</p>
</li>
<li><p><strong>Layer 9 — MCPs.</strong> External tool servers (Slack, Linear, Notion, your DB, your CI, …).</p>
</li>
</ul>
<blockquote>
<p>Mental model: <strong>the harness loads layers in order, and later layers override earlier ones.</strong> Project beats home. Local beats committed. Skills beat raw prompts. That's the whole game.</p>
</blockquote>
<hr />
<h2>Layer 0 — The LLM (the brain)</h2>
<ul>
<li><p>This is the model weights themselves: Claude Opus 4.6, Sonnet 4.6, Haiku 4.5, etc.</p>
</li>
<li><p>The LLM has <strong>no idea your project exists</strong> until the harness ships it the right context.</p>
</li>
<li><p>It is <em>swappable</em>. The harness doesn't care which brain it's talking to as long as the API is Anthropic-compatible.</p>
</li>
</ul>
<p><strong>Alternatives:</strong></p>
<ul>
<li><p><strong>Hosted frontier:</strong> GPT-5 / o-series (OpenAI), Gemini 2.5 (Google), Grok (xAI), Mistral Large.</p>
</li>
<li><p><strong>Open-weights, local:</strong> Llama 4 / 3.x (Meta), Qwen 3 (Alibaba), DeepSeek-R1 / V3, Mixtral.</p>
</li>
<li><p><strong>Aggregators:</strong> OpenRouter (one key, 100+ models), Together AI, Fireworks, Groq.</p>
</li>
</ul>
<p><strong>Real-world analogy:</strong> the LLM is a brilliant freelance contractor. Smart, but useless without the keys to the building, the tools, and a brief.</p>
<hr />
<h2>Layer 1 — The Harness (Claude Code itself)</h2>
<p>A <strong>harness</strong> is the runtime around the LLM:</p>
<ul>
<li><p>The <strong>agentic loop</strong> — read prompt → think → call tool → observe → repeat until done.</p>
</li>
<li><p>The <strong>tool layer</strong> — Read, Write, Edit, Bash, Grep, Glob, WebFetch.</p>
</li>
<li><p>The <strong>context manager</strong> — what's in the prompt, what got truncated, what's cached.</p>
</li>
<li><p>The <strong>safety/permission layer</strong> — which folders Claude can touch, which commands need approval.</p>
</li>
</ul>
<p><strong>Alternatives:</strong></p>
<ul>
<li><p><strong>Terminal harnesses:</strong> Mastra Code (TypeScript-extensible, "never compacts"), Pi (pi.dev, minimalist), Aider (mature, token-efficient), Codex CLI (OpenAI), Gemini CLI (Google), Cline / Roo Code, OpenCode.</p>
</li>
<li><p><strong>IDE harnesses:</strong> Cursor, Continue.dev, Windsurf, Zed AI, JetBrains AI.</p>
</li>
<li><p><strong>Background / multi-agent:</strong> Devin, Factory, Sweep, OMC (oh-my-claudecode multi-CLI orchestration).</p>
</li>
</ul>
<p><strong>Analogy:</strong> if the LLM is the contractor, the harness is the <strong>work order, the toolbox, and the foreman</strong> combined. Same contractor, different harness, completely different output.</p>
<p><strong>Diagram 2 — Harness anatomy:</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/5ffd78174084696c9d22a46f/ad3deebd-cfaa-483e-8998-3365c6287e39.png" alt="" style="display:block;margin:0 auto" />

<hr />
<h2>Layer 2 — <code>~/.claude/</code> (your home base)</h2>
<p>Lives in your home directory. Everything here applies to <strong>every project on this machine</strong>.</p>
<p>Typical contents:</p>
<ul>
<li><p><code>~/.claude/settings.json</code> — global preferences, default model, hook config.</p>
</li>
<li><p><code>~/.claude/CLAUDE.md</code> — global memory ("I prefer pnpm over npm. I work in PT.").</p>
</li>
<li><p><code>~/.claude/agents/</code> — your personal subagents.</p>
</li>
<li><p><code>~/.claude/commands/</code> — slash commands available everywhere.</p>
</li>
<li><p><code>~/.claude/plugins/</code> — installed plugins.</p>
</li>
<li><p><code>~/.claude/hooks/</code> — your global hooks.</p>
</li>
</ul>
<p><strong>Alternatives (same idea, different tool):</strong></p>
<ul>
<li><p><code>~/.codex/</code> and <code>~/.codex/AGENTS.md</code> (Codex CLI).</p>
</li>
<li><p><code>~/.gemini/</code> and <code>GEMINI.md</code> (Gemini CLI).</p>
</li>
<li><p><code>~/.aider.conf.yml</code> + <code>~/.aider/</code> (Aider).</p>
</li>
<li><p><code>~/.cursor/rules/</code> + <code>~/.cursor/mcp.json</code> (Cursor).</p>
</li>
<li><p><code>~/.continue/</code> (Continue.dev).</p>
</li>
<li><p><code>~/.config/mastra/</code> (Mastra Code).</p>
</li>
</ul>
<p><strong>Analogy:</strong> this is your <strong>personal toolbox in the truck</strong>. You bring it to every job site.</p>
<hr />
<h2>Layer 3 — Project <code>.claude/</code> folder</h2>
<p>A <code>.claude/</code> directory inside your repo. Same shape as <code>~/.claude/</code>, but <strong>scoped to this project</strong> and committed to git so the team gets the same setup.</p>
<ul>
<li><p><code>.claude/agents/</code> — project-specific subagents (e.g. <code>db-migration-reviewer</code>).</p>
</li>
<li><p><code>.claude/commands/</code> — project slash commands (<code>/deploy-staging</code>).</p>
</li>
<li><p><code>.claude/skills/</code> — project skills (e.g. "how we write Postgres migrations").</p>
</li>
<li><p><code>.claude/hooks/</code> — project hooks (e.g. block commits without a Linear ticket).</p>
</li>
<li><p><code>.claude/settings.json</code> — project model overrides, allowed tools.</p>
</li>
</ul>
<p><strong>Alternatives (same idea, different tool):</strong></p>
<ul>
<li><p><code>.cursor/rules/</code> and <code>.cursor/mcp.json</code> (Cursor).</p>
</li>
<li><p><code>.codex/</code> (Codex CLI).</p>
</li>
<li><p><code>.gemini/</code> (Gemini CLI).</p>
</li>
<li><p><code>.aider/</code> + <code>.aider.conf.yml</code> + <code>CONVENTIONS.md</code> (Aider).</p>
</li>
<li><p><code>.continue/</code> (Continue.dev).</p>
</li>
<li><p><code>.mastra/</code> (Mastra Code, project-scoped threads + config).</p>
</li>
<li><p><code>.vscode/settings.json</code> for AI extensions.</p>
</li>
</ul>
<p><strong>Analogy:</strong> the <strong>shared toolbox in the workshop</strong> that everyone on the team uses for <em>this</em> job.</p>
<hr />
<h2>Layer 4 — The <code>CLAUDE.md</code> family (memory)</h2>
<p>This is the layer most people get wrong. There are three flavors and they cascade:</p>
<ul>
<li><p><code>~/.claude/CLAUDE.md</code> — your global memory. Survives across all projects.</p>
</li>
<li><p><code>&lt;repo&gt;/CLAUDE.md</code> — project memory. <strong>Committed to git.</strong> Team-shared.</p>
</li>
<li><p><code>&lt;repo&gt;/CLAUDE.local.md</code> — your personal project notes. <strong>Gitignored.</strong> Don't commit.</p>
</li>
</ul>
<p>Order of precedence at session start (most general → most specific):</p>
<p><code>~/.claude/CLAUDE.md</code> → <code>&lt;repo&gt;/CLAUDE.md</code> → <code>&lt;repo&gt;/CLAUDE.local.md</code></p>
<p>What actually goes in each:</p>
<ul>
<li><p><strong>Global</strong>: your preferences, package manager defaults, communication style.</p>
</li>
<li><p><strong>Project committed</strong>: architecture overview, repo conventions, "we use Drizzle, not Prisma," critical gotchas.</p>
</li>
<li><p><strong>Project local</strong>: your local DB creds path, in-progress refactor notes, "don't touch X until Friday."</p>
</li>
</ul>
<p><strong>Alternatives (the "memory file" name in other harnesses):</strong></p>
<ul>
<li><p><code>AGENTS.md</code> — increasingly the cross-tool standard. Used by OpenAI Codex, Cursor, Aider, and others; many tools now read both <code>AGENTS.md</code> and their own file.</p>
</li>
<li><p><code>GEMINI.md</code> — Gemini CLI's equivalent.</p>
</li>
<li><p><code>.cursorrules</code> (legacy) → <code>.cursor/rules/*.md</code> (current) — Cursor's project-rules format.</p>
</li>
<li><p><code>CONVENTIONS.md</code> — Aider's preferred file (<code>--read CONVENTIONS.md</code>).</p>
</li>
<li><p><strong>System prompt files</strong> — Continue.dev and most TypeScript frameworks (Mastra) put this in code.</p>
</li>
</ul>
<blockquote>
<p>Pro tip: many teams now keep one <code>AGENTS.md</code> and symlink <code>CLAUDE.md</code> / <code>GEMINI.md</code> to it so every harness picks it up.</p>
</blockquote>
<p><strong>Analogy:</strong> think of CLAUDE.md as <strong>the onboarding doc you'd hand a new contractor</strong>. Global = "how I work." Project = "how the team works." Local = "what's weird about <em>my</em> setup right now."</p>
<p><strong>Diagram 3 — The CLAUDE.md cascade:</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/5ffd78174084696c9d22a46f/d6430d09-463e-4ea2-be8e-507f8c775eda.png" alt="" style="display:block;margin:0 auto" />

<hr />
<h2>Layer 5 — Plugins</h2>
<p>A <strong>plugin</strong> is a packaged bundle that can include any combination of: skills, subagents, slash commands, hooks, and MCP servers. Install one plugin and you get a coordinated set of capabilities.</p>
<p>Examples you can install today:</p>
<ul>
<li><p><strong>Superpowers</strong> — discipline plugin: TDD, systematic debugging, brainstorming, plan-writing, parallel agents. Very opinionated — it <em>forces</em> you into rigorous workflows.</p>
</li>
<li><p><strong>oh-my-claudecode (OMC)</strong> — multi-agent orchestration plugin. ~19 specialized agents, ~36 skills, runs Claude / Codex / Gemini in parallel tmux panes for cross-validation. Marketed as zero-config + 30–50% token savings.</p>
</li>
<li><p><strong>gstack</strong> — Garry Tan's (YC President) personal Claude Code stack, 23 opinionated tools modeling <strong>CEO / Designer / Eng Manager / Release Manager / Doc Engineer / QA</strong> roles. Workflow: <em>Think → Plan → Build → Review → Test → Ship → Reflect</em>.</p>
</li>
<li><p><strong>caveman</strong> — token-reduction plugin (more on this below).</p>
</li>
</ul>
<p><strong>Alternatives (plugin/extension ecosystems in other harnesses):</strong></p>
<ul>
<li><p><strong>Cursor</strong> — Cursor Rules library + Cursor extensions marketplace.</p>
</li>
<li><p><strong>Continue.dev</strong> — <code>config.yaml</code> blocks + community config presets.</p>
</li>
<li><p><strong>Aider</strong> — no plugin system by design; you compose via <code>CONVENTIONS.md</code> + git hooks.</p>
</li>
<li><p><strong>Codex CLI</strong> — slash-command bundles, AGENTS.md presets.</p>
</li>
<li><p><strong>Pi</strong> — first-class <strong>pi packages</strong> (third-party harness extensions).</p>
</li>
<li><p><strong>Mastra Code</strong> — extend programmatically with custom modes, tools, subagents in TypeScript.</p>
</li>
</ul>
<p><strong>Analogy:</strong> plugins are like <strong>buying a kitchen appliance bundle</strong> instead of one mixer at a time — they come pre-wired together.</p>
<hr />
<h2>Layer 6 — Skills (the secret sauce)</h2>
<p>A <strong>skill</strong> is a folder with a <code>SKILL.md</code> file (and optional helper scripts/templates). It tells Claude <em>how</em> to do a specific task — the recipe, not the ingredients.</p>
<ul>
<li><p>Skills load <strong>on demand</strong> when their description matches your request — that's "progressive disclosure" and it's why skills don't blow up your context window.</p>
</li>
<li><p>Skills can be standalone in <code>.claude/skills/</code> or bundled inside plugins.</p>
</li>
<li><p>A great skill is a checklist + a few examples + a link to deeper docs the LLM can read if needed.</p>
</li>
</ul>
<p><strong>Alternatives (the "on-demand instructions" pattern):</strong></p>
<ul>
<li><p><strong>Cursor</strong> — Project Rules / User Rules with glob-based scoping.</p>
</li>
<li><p><strong>Pi</strong> — Skills are first-class, designed not to bust the prompt cache.</p>
</li>
<li><p><strong>Mastra Code</strong> — Skills + custom commands.</p>
</li>
<li><p><strong>gstack</strong> — slash commands as opinionated mini-skills.</p>
</li>
<li><p><strong>Aider</strong> — no equivalent; you'd inline guidance in <code>CONVENTIONS.md</code>.</p>
</li>
<li><p><strong>Continue.dev</strong> — prompt template files + <code>.prompt</code> blocks.</p>
</li>
</ul>
<p><strong>Analogy:</strong> skills are <strong>standard operating procedures</strong> posted on the wall. The contractor (LLM) reads the relevant SOP only when they're about to do that task — they don't memorize all of them up front.</p>
<hr />
<h2>Layer 7 — Subagents</h2>
<p>A <strong>subagent</strong> is a separate Claude instance you dispatch with a focused task and a fresh context window. It returns a summary; the bulk of its working memory never pollutes yours.</p>
<p>Why this matters:</p>
<ul>
<li><p><strong>Context isolation</strong> — a 50k-token research dive doesn't bloat your main session.</p>
</li>
<li><p><strong>Specialization</strong> — <code>code-reviewer</code>, <code>Explore</code>, <code>Plan</code>, custom domain reviewers.</p>
</li>
<li><p><strong>Parallelism</strong> — fire several at once for independent tasks.</p>
</li>
</ul>
<p><strong>Alternatives (multi-agent / delegation patterns):</strong></p>
<ul>
<li><p><strong>Mastra Code</strong> — custom subagents in TypeScript via the Agent primitive.</p>
</li>
<li><p><strong>Mastra (framework)</strong> — full agent + workflow graph (DAGs, branches, parallelism).</p>
</li>
<li><p><strong>OMC (oh-my-claudecode)</strong> — runs Claude + Codex + Gemini as parallel CLI panes for cross-validation.</p>
</li>
<li><p><strong>Cline / Roo Code</strong> — built-in "Plan/Act" modes acting as lightweight subagents.</p>
</li>
<li><p><strong>CrewAI / AutoGen / LangGraph</strong> — framework-land alternatives if you're building outside a CLI.</p>
</li>
<li><p><strong>Pi</strong> — none by default (philosophical); add via packages.</p>
</li>
<li><p><strong>Aider</strong> — single-agent; uses architect/editor <em>roles</em> on one model instead.</p>
</li>
</ul>
<p><strong>Analogy:</strong> delegating to a <strong>specialist consultant</strong>. They go off, do the deep work, and email you a one-pager.</p>
<p><strong>Diagram 4 — Subagent fan-out (main session delegates, gets summaries back):</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/5ffd78174084696c9d22a46f/640ce8ea-37fb-4413-82e7-4abf443f7ab2.png" alt="" style="display:block;margin:0 auto" />

<hr />
<h2>Layer 8 — Hooks (the middleware)</h2>
<p>Hooks are scripts that run automatically on lifecycle events. They're the <strong>interceptors</strong> of Claude Code.</p>
<p>Common hook points:</p>
<ul>
<li><p><code>SessionStart</code> — runs when you launch Claude Code (e.g. inject today's date, current branch, recent commits).</p>
</li>
<li><p><code>PreToolUse</code> — fires before any tool call. Can block, modify args, or warn.</p>
</li>
<li><p><code>PostToolUse</code> — fires after. Great for auto-formatting, logging, or running tests after Edit.</p>
</li>
<li><p><code>UserPromptSubmit</code> — fires when you send a message. Inject extra context here.</p>
</li>
<li><p><code>Stop</code> <strong>/</strong> <code>SubagentStop</code> — cleanup, summarization, notifications.</p>
</li>
</ul>
<p>What they're great for:</p>
<ul>
<li><p>Auto-running <code>prettier</code> / <code>ruff</code> after every Edit.</p>
</li>
<li><p>Blocking writes outside the project root.</p>
</li>
<li><p>Posting a Slack message when a long task finishes.</p>
</li>
<li><p>Injecting "what changed since my last commit" into every session.</p>
</li>
</ul>
<p><strong>Alternatives (lifecycle / middleware in other tools):</strong></p>
<ul>
<li><p><strong>Mastra Code</strong> — has its own hooks layer (pre/post tool, session lifecycle).</p>
</li>
<li><p><strong>Aider</strong> — built-in lint/test commands + git hooks at the OS layer.</p>
</li>
<li><p><strong>Cursor</strong> — auto-format-on-save and limited "background tasks."</p>
</li>
<li><p><strong>Pi</strong> — extend via skills + scripts (no formal hooks API).</p>
</li>
<li><p><strong>OS / git hooks</strong> — <code>pre-commit</code>, <code>pre-push</code>, Husky — work with <em>any</em> harness.</p>
</li>
<li><p><strong>Continue.dev</strong> — slash commands + indexer hooks.</p>
</li>
</ul>
<p><strong>Analogy:</strong> hooks are the <strong>building's automation system</strong> — motion-activated lights, fire alarms, key-card locks. Claude doesn't think about them; they just fire on the right event.</p>
<hr />
<h2>Layer 9 — MCPs (external tools)</h2>
<p><strong>Model Context Protocol</strong> servers expose external systems to Claude as callable tools — Slack, Linear, Notion, GitHub, your database, your internal API, etc.</p>
<ul>
<li><p>An MCP server defines tools + their JSON schemas.</p>
</li>
<li><p>The harness loads them, hands the schemas to the LLM, and proxies calls.</p>
</li>
<li><p>Plugins frequently bundle MCPs (e.g. a "PM plugin" might bundle Linear + Notion + Figma MCPs).</p>
</li>
</ul>
<p><strong>Alternatives (how each ecosystem exposes external tools):</strong></p>
<ul>
<li><p><strong>MCP (cross-tool standard)</strong> — supported by Claude Code, Cursor, Mastra Code, Continue, Codex, Gemini CLI, Cline, and most modern harnesses. The closest thing to a "USB-C for AI tools."</p>
</li>
<li><p><strong>OpenAI function calling / Tools API</strong> — the older, ecosystem-specific equivalent.</p>
</li>
<li><p><strong>LangChain / LlamaIndex tools</strong> — framework-side abstractions.</p>
</li>
<li><p><strong>Mastra tools</strong> — TypeScript-native tool definitions in the Mastra framework.</p>
</li>
<li><p><strong>Aider</strong> — minimal; relies on shell + git instead of external connectors.</p>
</li>
<li><p><strong>Custom REST/GraphQL</strong> — wrap anything as an MCP server with one of the SDKs.</p>
</li>
</ul>
<p><strong>Analogy:</strong> MCPs are the <strong>utility hookups</strong> — water, gas, electricity, internet. The building (harness) is useless without them once you want to do real work.</p>
<hr />
<h2>How everything stacks together</h2>
<p>The thing that confuses people: <em>all these layers are active at once.</em> Here's the mental order on every turn:</p>
<ol>
<li><p>The <strong>LLM</strong> receives a prompt.</p>
</li>
<li><p>The <strong>harness</strong> has prepended: system prompt + global CLAUDE.md + project CLAUDE.md + local CLAUDE.md + active skills' descriptions + tool schemas (incl. MCPs).</p>
</li>
<li><p><strong>Hooks</strong> may have already run (SessionStart, UserPromptSubmit) and injected extra context.</p>
</li>
<li><p>The LLM picks a tool. <strong>PreToolUse hooks</strong> fire — they can block or modify.</p>
</li>
<li><p>Tool runs. <strong>PostToolUse hooks</strong> fire.</p>
</li>
<li><p>If the task is gnarly, the LLM <strong>dispatches a subagent</strong>, which runs the same loop in isolation.</p>
</li>
<li><p>Repeat until done. <strong>Stop hooks</strong> fire.</p>
</li>
</ol>
<blockquote>
<p>Plugins like Superpowers + your own agents play nicely together because they live in <strong>different layers</strong>: Superpowers ships skills + commands; your custom agents live in <code>.claude/agents/</code>; your hooks live in <code>.claude/hooks/</code>. They compose because the harness merges them at load time.</p>
</blockquote>
<p><strong>Diagram 5 — One full turn, including hooks firing:</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/5ffd78174084696c9d22a46f/f04e86e5-e79c-4d1c-83ce-ae1ee19ee5bb.png" alt="" style="display:block;margin:0 auto" />

<hr />
<h2>The framework ecosystem (the "configs on top of configs")</h2>
<p>This is where it gets interesting. People are building <strong>opinionated stacks</strong> on top of Claude Code, the way oh-my-zsh sits on top of zsh.</p>
<h3>Superpowers</h3>
<ul>
<li><p>A discipline-first plugin. Strong opinions on TDD, systematic debugging, plan-before-code, evidence-before-claims.</p>
</li>
<li><p>Forces you to <em>use the skill</em> even when you think you don't need it.</p>
</li>
<li><p>Best for: solo devs and small teams who want guardrails against "vibe coding."</p>
</li>
</ul>
<h3>oh-my-claudecode (OMC)</h3>
<ul>
<li><p>Multi-agent orchestration. Ships ~19 agents and ~36 skills.</p>
</li>
<li><p>Runs Claude + Codex + Gemini CLIs in parallel tmux panes — great for cross-checking architecture, security review, deep code analysis.</p>
</li>
<li><p>Marketed at 30–50% token savings via specialization.</p>
</li>
</ul>
<h3>gstack (Garry Tan / YC)</h3>
<ul>
<li><p>23 tools modeling startup roles. The skills <code>/office-hours</code>, <code>/review</code>, <code>/ship</code> are the headline features.</p>
</li>
<li><p>Workflow is the product: <em>Think → Plan → Build → Review → Test → Ship → Reflect.</em></p>
</li>
<li><p>Tan claims ~10K logical LOC/week throughput while running YC full-time. Take the number with a grain of salt; the workflow itself is the real artifact.</p>
</li>
</ul>
<h3>caveman</h3>
<ul>
<li><p>Token-reduction skill. Forces Claude to drop articles, filler words, hedging — output reads like cave drawings.</p>
</li>
<li><p>Reported ~65% drop in average response tokens (294 vs 1,214 across 11 dev tasks); spread of 22–87% depending on prompt.</p>
</li>
<li><p>Bonus: a March 2026 paper ("Brevity Constraints Reverse Performance Hierarchies in Language Models") found brevity-constrained models <em>gained</em> up to 26pp in accuracy on some benchmarks. Less can literally be more.</p>
</li>
</ul>
<blockquote>
<p>All four can be combined. Superpowers for discipline + gstack for workflow + caveman for compression + your own <code>.claude/</code> for project-specific glue.</p>
</blockquote>
<hr />
<h2>Swapping the brain: OpenRouter and Ollama</h2>
<p>Claude Code ships pointed at Anthropic's API, but the harness only cares about an Anthropic-compatible endpoint. Two main routes:</p>
<h3>Option A — Claude Code Router (CCR)</h3>
<ul>
<li><p>A community proxy that translates Claude Code's API calls to OpenRouter, OpenAI, Gemini, DeepSeek, Ollama, etc.</p>
</li>
<li><p>You set <code>ANTHROPIC_BASE_URL</code> (and an API key) to point at the router.</p>
</li>
<li><p>Per-task routing: cheap model for trivial edits, frontier model for hard reasoning.</p>
</li>
</ul>
<h3>Option B — LiteLLM proxy</h3>
<ul>
<li>Same idea, more general-purpose. Spins up an OpenAI-/Anthropic-compatible endpoint that fans out to ~100+ providers.</li>
</ul>
<h3>Option C — Ollama (fully local)</h3>
<ul>
<li><p>Run a model locally — Llama 3.x, Qwen, DeepSeek-R1 distills, etc.</p>
</li>
<li><p>Stick LiteLLM (or CCR) in front of Ollama to expose an Anthropic-compatible API.</p>
</li>
<li><p>Trade-off: zero API cost, full privacy; but tool-use quality varies wildly by model. Frontier-class agentic behavior is still a stretch on consumer hardware.</p>
</li>
</ul>
<p><strong>Quick recipe (conceptual):</strong></p>
<ul>
<li><p><code>OLLAMA_HOST=...</code> runs the model.</p>
</li>
<li><p>LiteLLM exposes <code>http://localhost:4000</code> as Anthropic-compatible.</p>
</li>
<li><p><code>export ANTHROPIC_BASE_URL=http://localhost:4000</code> and <code>export ANTHROPIC_AUTH_TOKEN=anything</code>.</p>
</li>
<li><p>Launch Claude Code. Same harness, different brain.</p>
</li>
</ul>
<p><strong>Analogy:</strong> the harness is the <strong>car</strong>. Claude is the default engine. OpenRouter and Ollama let you swap the engine without rebuilding the chassis. The steering wheel (your skills, hooks, CLAUDE.md) doesn't care which engine you bolted in.</p>
<blockquote>
<p>⚠️ Caveat: smaller open models often struggle with multi-tool workflows, file editing precision, and long contexts. For local dev you're often best with a hybrid — Ollama for cheap autocomplete-like loops, Claude/Anthropic for the hard stuff.</p>
</blockquote>
<p><strong>Diagram 6 — Same harness, swappable brain:</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/5ffd78174084696c9d22a46f/ba7b4a3f-375c-4cc4-9653-f4a7e8601cc6.png" alt="" style="display:block;margin:0 auto" />

<hr />
<h2>Comparing harnesses: Claude Code vs Mastra Code vs Pi</h2>
<p>You asked which harness uses fewer tokens. Short answer: <strong>it's not the harness, it's the discipline layered on top.</strong> But the three harnesses have real personality differences.</p>
<blockquote>
<p>⚠️ <strong>Quick disambiguation.</strong> <em>Mastra</em> is the TypeScript <strong>framework</strong> for building AI apps and agents (think SDK + primitives: Harness, Agent, Memory). <em>Mastra Code</em> is a separate <strong>terminal coding agent</strong> built <strong>on top of</strong> Mastra's primitives — it's the direct apples-to-apples comparison to Claude Code. Below I'm comparing <strong>Mastra Code</strong>, the harness, not the framework.</p>
</blockquote>
<h3>Claude Code</h3>
<ul>
<li><p>Heaviest by default — rich tool layer, MCPs, plugins, subagents, hooks all in one box.</p>
</li>
<li><p>Best ergonomics for humans; biggest ecosystem (Superpowers, OMC, gstack, caveman).</p>
</li>
<li><p>Token cost can creep up fast unless you use skills + caveman + subagents to isolate context.</p>
</li>
<li><p>Memory model: prompt-based (CLAUDE.md cascade) + auto-compaction when the context window fills.</p>
</li>
</ul>
<h3>Mastra Code (code.mastra.ai)</h3>
<ul>
<li><p>Terminal-based coding agent built on Mastra's Harness / Agent / Memory primitives.</p>
</li>
<li><p><strong>Headline feature: "never compacts."</strong> Uses <em>observational memory</em> — it watches, reflects, and compresses context as it goes, instead of doing a hard truncation when the window fills. The pitch: long-running sessions stay precise.</p>
</li>
<li><p>Three explicit modes — <strong>Build / Plan / Fast</strong> — so you pick a different cost/quality profile per task.</p>
</li>
<li><p>Supports MCP servers, hooks, custom commands, skills, project-scoped threads.</p>
</li>
<li><p>Programmatically extensible: custom modes, tools, subagents, storage all in TypeScript.</p>
</li>
<li><p>Connects to "thousands" of models out of the box (you bring the API key).</p>
</li>
<li><p>Requires Node 22.13+.</p>
</li>
<li><p>Strength: longest-running sessions stay coherent without manual <code>/compact</code>. Weakness: smaller ecosystem of community plugins than Claude Code today.</p>
</li>
</ul>
<h3>Pi (pi.dev, by Mario Zechner)</h3>
<ul>
<li><p>Minimalist terminal coding harness. Explicit non-goal: features like sub-agents and plan mode are <em>not</em> built in — you add them only if you need them.</p>
</li>
<li><p>Skills are first-class, loaded on-demand without breaking the prompt cache.</p>
</li>
<li><p>Self-modifying philosophy: Pi can rewrite Pi to fit your workflow.</p>
</li>
<li><p>Often the lowest token-per-task in head-to-head comparisons because there's less ambient harness overhead — but you pay for it in setup time.</p>
</li>
</ul>
<h3>Rough trade-off table</h3>
<ul>
<li><p><strong>Most plug-and-play</strong> → Claude Code.</p>
</li>
<li><p><strong>Best at long, never-compact sessions</strong> → Mastra Code (observational memory).</p>
</li>
<li><p><strong>Lowest baseline token spend</strong> → Pi (especially with caveman bolted on).</p>
</li>
<li><p><strong>Best for multi-agent orchestration</strong> → Claude Code with OMC, or Mastra Code with custom subagents.</p>
</li>
<li><p><strong>Easiest to extend in TypeScript</strong> → Mastra Code.</p>
</li>
</ul>
<blockquote>
<p>The honest take: <em>no harness is intrinsically cheaper.</em> What matters is (a) how much system prompt you stuff, (b) how aggressively you use subagents to isolate context, (c) whether your output style is verbose by default, and (d) whether your harness compacts (Claude Code) or compresses-as-it-goes (Mastra Code) — the latter avoids the cliff where Claude Code "forgets" mid-session.</p>
</blockquote>
<hr />
<h2>Token economics (and where caveman fits)</h2>
<p>Where tokens actually go in a Claude Code session:</p>
<ul>
<li><p><strong>System prompt + harness instructions</strong> — fixed overhead per turn.</p>
</li>
<li><p><strong>CLAUDE.md cascade</strong> — grows with every doc you add.</p>
</li>
<li><p><strong>Active skills' descriptions</strong> — minor; only triggered skills load full content.</p>
</li>
<li><p><strong>Tool schemas (esp. MCPs)</strong> — <em>often the biggest invisible cost.</em> Loading 200 MCP tool schemas can dwarf your prompt.</p>
</li>
<li><p><strong>Conversation history</strong> — grows linearly. Caching helps.</p>
</li>
<li><p><strong>Output tokens</strong> — what caveman attacks.</p>
</li>
</ul>
<p>Practical levers:</p>
<ul>
<li><p><strong>Prune MCPs aggressively.</strong> If you don't use a connector this session, don't load it.</p>
</li>
<li><p><strong>Move long instructions into skills</strong>, not CLAUDE.md, so they only load on demand.</p>
</li>
<li><p><strong>Use subagents for research</strong> — keep their summary, not their working memory.</p>
</li>
<li><p><strong>Run caveman</strong> when your task is well-defined and you don't need verbose explanations.</p>
</li>
<li><p><strong>Cache the system prompt.</strong> Anthropic's prompt caching cuts repeat-prompt cost dramatically.</p>
</li>
</ul>
<hr />
<h2>Putting it all together</h2>
<p>You don't pick <em>one</em> layer — you compose them. A reasonable "power user" setup:</p>
<ul>
<li><p><strong>Brain:</strong> Claude (default) for hard tasks, Ollama via LiteLLM for cheap loops.</p>
</li>
<li><p><strong>Harness:</strong> Claude Code.</p>
</li>
<li><p><strong>Home:</strong> <code>~/.claude/CLAUDE.md</code> with your prefs, plus Superpowers + caveman installed globally.</p>
</li>
<li><p><strong>Project:</strong> <code>.claude/</code> with team CLAUDE.md, project skills, a <code>code-reviewer</code> subagent, a <code>pre-commit</code> hook.</p>
</li>
<li><p><strong>Personal:</strong> <code>CLAUDE.local.md</code> for your in-progress quirks.</p>
</li>
<li><p><strong>Workflow:</strong> gstack-style Think → Plan → Build → Review → Test → Ship.</p>
</li>
</ul>
<p><strong>Analogy to close on:</strong> Claude Code isn't a tool — it's a <strong>building you can renovate</strong>. The LLM is rented power. The harness is the structure. Your <code>.claude/</code> folders are the floors you actually live on. Plugins are pre-fab kitchens. Skills are recipes pinned to the fridge. Subagents are interns. Hooks are the smart-home automation. MCPs are the utilities.</p>
<p>You don't have to build everything yourself — but you do have to know which floor the light switch is on.</p>
<hr />
<h2>Sources &amp; further reading</h2>
<ul>
<li><p><a href="https://github.com/juliusbrussee/caveman">caveman GitHub (JuliusBrussee)</a></p>
</li>
<li><p><a href="https://www.mayhemcode.com/2026/04/caveman-claude-code-how-to-save-tokens.html">Caveman Claude Code: How to Save Tokens</a></p>
</li>
<li><p><a href="https://www.nathanonn.com/claude-code-caveman-mode/">Claude Code Caveman Mode: Save &gt;75% on Usage</a></p>
</li>
<li><p><a href="https://github.com/yeachan-heo/oh-my-claudecode">oh-my-claudecode (yeachan-heo)</a></p>
</li>
<li><p><a href="https://ohmyclaudecode.com/">oh-my-claudecode website</a></p>
</li>
<li><p><a href="https://github.com/garrytan/gstack">gstack (Garry Tan)</a></p>
</li>
<li><p><a href="https://www.sitepoint.com/gstack-garry-tan-claude-code/">GStack Tutorial — SitePoint</a></p>
</li>
<li><p><a href="https://www.mindstudio.ai/blog/what-is-gstack-gary-tan-claude-code-framework">What Is GStack? — MindStudio</a></p>
</li>
<li><p><a href="https://pi.dev/">Pi Coding Agent (pi.dev)</a></p>
</li>
<li><p><a href="https://github.com/badlogic/pi-mono">pi-mono on GitHub</a></p>
</li>
<li><p><a href="https://mariozechner.at/posts/2025-11-30-pi-coding-agent/">What I learned building an opinionated, minimal coding agent</a></p>
</li>
<li><p><a href="https://code.mastra.ai/">Mastra Code (code.mastra.ai)</a></p>
</li>
<li><p><a href="https://mastra.ai/blog/announcing-mastra-code">Announcing Mastra Code: A coding agent that never compacts</a></p>
</li>
<li><p><a href="https://mastra.ai/docs/mastra-code/configuration">Mastra Code Configuration docs</a></p>
</li>
<li><p><a href="https://github.com/mastra-ai/mastra">Mastra (the framework) on GitHub</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Getting Started with Claude Code — And Why It's Not Just Another Cursor]]></title><description><![CDATA[I've been coding for 14+ years. I've used many IDEs, every AI tool (Devin to Qoder, Lovable,. ), every "next big thing."
When I switched to Claude Code, it felt different. Not "oh cool, another copilo]]></description><link>https://tech.sedhu.me/getting-started-with-claude-code-and-why-it-s-not-just-another-cursor</link><guid isPermaLink="true">https://tech.sedhu.me/getting-started-with-claude-code-and-why-it-s-not-just-another-cursor</guid><dc:creator><![CDATA[Sedhu]]></dc:creator><pubDate>Wed, 25 Mar 2026 06:20:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/5ffd78174084696c9d22a46f/b3876f79-d887-4223-979f-af2a912b8a43.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I've been coding for 14+ years. I've used many IDEs, every AI tool (Devin to Qoder, Lovable,. ), every "next big thing."</p>
<p>When I switched to Claude Code, it felt different. Not "oh cool, another copilot" different — more like going from a GPS that suggests routes to a co-driver who actually drives. 🚗</p>
<p>Here's everything I wish someone told me on Day 1.</p>
<hr />
<h2>First — How is Claude Code Different from Cursor?</h2>
<p>Cursor is great. I've used it. But here's the honest comparison:</p>
<p><strong>Cursor lives inside VS Code.</strong> It's an IDE-first experience — you get AI autocomplete, inline chat, and a composer tab. Think of it like having a smart assistant sitting inside your office.</p>
<p><strong>Claude Code lives in your terminal.</strong> It's an agentic coding tool — it doesn't just suggest code, it <em>executes</em> things. It reads your codebase, runs commands, makes git commits, creates PRs, and manages files. Think of it like hiring a junior dev who actually does the work, not just tells you what to do. 🧑‍💻</p>
<p>Here's what really sets Claude Code apart:</p>
<p>→ <strong>Plan mode + Skills</strong> — Cursor has plan mode too, but Claude Code takes it further. You can Shift+Tab to cycle between Normal mode (full agent), Plan mode (read-only research — Claude looks but doesn't touch), and Auto-accept mode (full speed, no permission prompts). It's like having gears in a car — you pick the level of control you want.</p>
<p>→ <strong>Hooks</strong> — This is something Cursor doesn't have. Claude Code lets you run custom scripts triggered by events (before/after tool use, on conversation start, etc.). Think of it like GitHub Actions but inside your coding agent. Pre-commit linting, auto-formatting, custom checks — all automatic.</p>
<p>→ <strong>Skills over Commands</strong> — Claude Code used to have "commands" (<code>.claude/commands/*.md</code>) and "skills" (<code>.claude/skills/*/SKILL.md</code>) as separate things. They've now been unified — skills ARE the slash command system. Important: <strong>commands are deprecated — use skills going forward.</strong> Skills support additional features like supporting files, and Claude can even auto-trigger them based on context rather than you typing a slash command.</p>
<p>→ <strong>Subagents</strong> — Claude Code can spawn specialized agents (Explore, Plan, Verify) to handle different parts of a task in parallel. Cursor doesn't have this concept. It's like having a team of specialists instead of one generalist.</p>
<hr />
<h2>The Commands You Need to Know</h2>
<p>I'm breaking these into levels because there's a lot, and you don't need everything on Day 1.</p>
<h3>🟢 Basic (Start Here)</h3>
<ul>
<li><p><code>/init</code> — First thing you run in any project. It generates a <code>CLAUDE.md</code> file — basically a cheat sheet that tells Claude about your codebase, conventions, and preferences. Think of it like onboarding a new team member.</p>
</li>
<li><p><code>/help</code> — Shows all available commands and skills. Your compass when you're lost.</p>
</li>
<li><p><code>/compact</code> — Compresses your conversation context to save tokens (and money). Long conversation getting expensive? Hit compact. Like clearing your browser cache but for AI context.</p>
</li>
<li><p><code>/clear</code> — Nuclear option. Wipes all conversation history. File edits remain, but the slate is clean.</p>
</li>
<li><p><code>/cost</code> — See how much your current session is costing. Always good to check.</p>
</li>
</ul>
<h3>🟡 Intermediate (Once You're Comfortable)</h3>
<ul>
<li><p><code>/batch</code> — Orchestrates large-scale changes across your codebase <em>in parallel</em>. Need to update 50 files? Don't do them one by one. Batch is like having 10 devs working on different files simultaneously.</p>
</li>
<li><p><code>/fork</code> — Copies your current conversation into a branch without touching the original. Like git branch, but for your AI conversation.</p>
</li>
<li><p><code>/rewind</code> — Rolls back your conversation (including code changes) to an earlier point. Your "undo" button when Claude goes in the wrong direction.</p>
</li>
<li><p><code>/review</code> — Runs a code review on your changes before you commit. Like having a senior dev glance at your PR before you push.</p>
</li>
<li><p><code>/mcp</code> — Exposes MCP (Model Context Protocol) operations as slash commands. This is how you connect Claude to external tools — GitHub, Slack, databases, anything.</p>
</li>
</ul>
<h3>🔴 Advanced (Power User Territory)</h3>
<ul>
<li><p><strong>Permission Modes</strong> — Five levels, cycle with Shift+Tab:</p>
<ul>
<li><p><strong>Default</strong> — Prompts for every potentially dangerous operation</p>
</li>
<li><p><strong>Auto-accept (acceptEdits)</strong> — No permission prompts for file edits. Full speed.</p>
</li>
<li><p><strong>Plan mode</strong> — Read-only. Claude researches and plans but can't modify anything.</p>
</li>
<li><p><strong>Don't Ask</strong> — Auto-denies everything not explicitly pre-approved</p>
</li>
<li><p><strong>Bypass</strong> — Skips ALL permission checks. Only use in isolated/sandboxed environments.</p>
</li>
</ul>
</li>
<li><p><strong>Subagents</strong> — Specialized agents you can spawn:</p>
<ul>
<li><p><strong>Explore</strong> — Fast codebase exploration (finding files, searching code)</p>
</li>
<li><p><strong>Plan</strong> — Software architect for designing implementation strategies</p>
</li>
<li><p><strong>Verify</strong> — Validation agent for checking work</p>
</li>
</ul>
</li>
<li><p><strong>Skills (replacing commands)</strong> — Create <code>.claude/skills/your-skill/SKILL.md</code> files to extend Claude's capabilities. Skills can be auto-triggered or manually invoked via <code>/your-skill</code>. You can build anything — code reviewers, deployment pipelines, content generators, testing frameworks.</p>
</li>
<li><p><code>/simplify</code> — Spawns three parallel review agents that check your recent changes for code reuse, quality, and efficiency issues, then fixes them. Like running three senior reviewers at once. 🔥</p>
</li>
<li><p><strong>CLAUDE.md</strong> — Not a command, but the most powerful concept. This markdown file at your project root is Claude's memory. Put your coding conventions, architecture decisions, tech stack preferences — everything. It's like writing an employee handbook for your AI teammate.</p>
</li>
</ul>
<hr />
<h2>GitHub Repos You Should Star Right Now ⭐</h2>
<p>These are the repos that'll 10x your Claude Code game:</p>
<p>→ <a href="https://github.com/jqueryscript/awesome-claude-code"><strong>Awesome Claude Code</strong></a> — The mothership. Curated list of tools, skills, plugins, integrations, and resources. If you only star one repo, make it this one.</p>
<p>→ <a href="https://github.com/garrytan/gstack"><strong>GStack by Garry Tan</strong></a> — 20K+ stars. Turns Claude Code into a virtual engineering team — CEO, designer, eng manager, QA lead, security officer, release engineer. 20 specialists, 8 power tools, all as slash commands. MIT license. It hit 10K stars in 48 hours. 🔥</p>
<p>→ <a href="https://github.com/gsd-build/gsd-2"><strong>Get Shit Done (GSD)</strong></a> — A spec-driven development workflow that prevents context rot. It externalizes state into files, splits work into small plans, executes each in fresh context, and verifies against explicit goals. Now a standalone CLI in v2.</p>
<p>→ <a href="https://github.com/msitarzewski/agency-agents"><strong>Agency Agents</strong></a> — 112+ specialized AI agent personas you can inject into Claude Code (and other tools). Think of it like hiring an entire agency — frontend wizards, QA leads, security reviewers, community managers — each with their own personality, processes, and deliverables. Built for Claude Code natively, works with the <code>.md</code> + YAML frontmatter format out of the box.</p>
<p>→ <a href="https://superset.sh/"><strong>Superset.sh</strong></a> — Want to run 10+ Claude Code agents in parallel? Superset orchestrates multiple Claude Code instances across isolated git worktrees — no merge conflicts, no stepping on each other's changes. It's like going from one developer to a whole engineering floor. Free tier available, Pro at $20/seat/month. (<a href="https://github.com/superset-sh/superset">GitHub</a>)</p>
<p>→ <a href="https://github.com/anthropics/claude-code"><strong>Claude Code Official Repo</strong></a> — The source. Star it, watch it, read the issues. This is where the roadmap lives.</p>
<hr />
<h2>Twitter/X Accounts to Follow 🐦</h2>
<p>→ <a href="https://x.com/bcherny"><strong>@bcherny (Boris Cherny)</strong></a> — The creator of Claude Code himself. He regularly shares tips, how he uses Claude Code, and behind-the-scenes insights from the team. His thread on "how I use Claude Code" is required reading. He started Claude Code as a side project in September 2024 — and today it writes 4% of all GitHub commits.</p>
<p>→ <a href="https://x.com/AnthropicAI"><strong>@AnthropicAI</strong></a> — Official Anthropic account for product announcements and updates.</p>
<p>→ <a href="https://x.com/alexalbert__"><strong>@alexalbert__</strong></a> — Alex Albert, head of Claude Relations at Anthropic. Great for understanding new features and model updates.</p>
<p>→ <a href="https://x.com/garaborosz"><strong>@garaborosz</strong></a> — Gábor, who curates a lot of Claude Code content and tutorials.</p>
<hr />
<h2>My Honest Take</h2>
<p>Claude Code isn't trying to be Cursor. Cursor is an AI-enhanced IDE. Claude Code is an AI agent that happens to code.</p>
<p>If you want autocomplete and inline suggestions while you type → Cursor. If you want an agent that reads your repo, plans architecture, writes code, runs tests, makes commits, and creates PRs → Claude Code.</p>
<p>Some people use both. And that's fine too.</p>
<p>The biggest mistake I see people make: they install Claude Code and use it like ChatGPT — just asking questions. That's like buying a Tesla and only using it in park. 🐴</p>
<p>Run <code>/init</code>. Set up your CLAUDE.md. Build custom skills. Use plan mode. Let it drive.</p>
<hr />
<h2>The #1 Rule of Thumb 🧠</h2>
<p>When in doubt — just ask Claude.</p>
<p>Seriously. Confused about a command? Ask Claude. Not sure which mode to use? Ask Claude. Don't know how to set up a skill? Ask Claude.</p>
<p>Claude Code is surprisingly good at explaining itself. It's like having a teammate who never gets annoyed when you ask "wait, how does this work again?" for the fifth time. Most people forget that the tool they're using IS the help desk. You don't need to Google "how to use Claude Code" — you can literally ask Claude Code.</p>
<p><strong>If you don't know, if you're confused, if you're stuck — your first move should always be: ask Claude.</strong></p>
<hr />
<h2>What's Next? Measure What Matters 📊</h2>
<p>Once you're up and running with Claude Code, the next step is setting up <strong>hooks</strong> to measure your productivity. Think of it like a fitness tracker — you've started working out, now track the gains.</p>
<p>GStack makes this easy with two powerful commands:</p>
<p>→ <code>/insights</code> — Gives you a breakdown of what Claude did, how much code was generated, and where time was spent. It's your personal sprint dashboard.</p>
<p>→ <code>/retro</code> — Runs a retrospective on your session — what went well, what didn't, and what to improve. Like doing a standup with your AI teammate after every coding session.</p>
<p>But before you retrospect — you need to actually see what Claude is doing under the hood. Two flags that'll change how you debug:</p>
<p>→ <code>--verbose</code> — Shows you every tool call, every file read, every decision Claude makes in real time. Think of it like turning on "show my work" mode in a math exam. You see the reasoning, not just the answer. Great for understanding <em>why</em> Claude did what it did.</p>
<p>→ <code>--debug</code> — Goes even deeper. Logs API calls, token counts, timing info, and internal state. This is your X-ray machine. When something goes wrong and you don't know why, <code>--debug</code> is the first flag you reach for.</p>
<p><strong>The workflow:</strong> Run your session with <code>--verbose</code>, then use <code>/insights</code> to see what happened, and <code>/retro</code> to reflect on it. It's like recording a game, watching the replay, and then doing a post-match analysis. 🎮</p>
<p>One more hidden gem:</p>
<p>→ <code>/btw</code> — This lets you inject a side note or additional context mid-conversation <em>without</em> interrupting Claude's current task. Think of it like passing a sticky note to someone while they're working — "btw, don't forget we use ESLint" or "btw, the API key is in .env.local." Claude absorbs the context and keeps going. Super useful when you remember something mid-flow.</p>
<p>You can't improve what you don't measure. Set up hooks, run <code>--verbose</code> to watch Claude work, <code>/insights</code> after every session, and <code>/retro</code> at the end of the week. That's how you go from "I use Claude Code" to "Claude Code makes me 10x." 🚀</p>
<hr />
<p>What's your experience been with Claude Code? Drop your favorite command or skill in the comments — I'm always looking for new tricks. 👇</p>
<p>#ClaudeCode #AIAgents #DeveloperTools #CodingWithAI #Anthropic #BuildInPublic  </p>
<img src="https://cdn.hashnode.com/uploads/covers/5ffd78174084696c9d22a46f/4cf426ec-35c3-46ec-b26f-5e1d72b2cef0.jpg" alt="" style="display:block;margin:0 auto" />

<p>credits : to the original creator</p>
]]></content:encoded></item><item><title><![CDATA[I Built a Self-Measuring Hook System for Claude Code — Here's What 427+ Automated Runs Taught Me]]></title><description><![CDATA[Most people using AI coding agents treat them like a magic autocomplete. Type a prompt, get code, ship it.
I did that too. Then things started breaking in weird ways — production code without tests, f]]></description><link>https://tech.sedhu.me/i-built-a-self-measuring-hook-system-for-claude-code-here-s-what-427-automated-runs-taught-me</link><guid isPermaLink="true">https://tech.sedhu.me/i-built-a-self-measuring-hook-system-for-claude-code-here-s-what-427-automated-runs-taught-me</guid><dc:creator><![CDATA[Sedhu]]></dc:creator><pubDate>Fri, 20 Mar 2026 08:15:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/5ffd78174084696c9d22a46f/da75e35b-b3d9-4ba0-9381-b7f987e78a47.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most people using AI coding agents treat them like a magic autocomplete. Type a prompt, get code, ship it.</p>
<p>I did that too. Then things started breaking in weird ways — production code without tests, force-pushes to main at 2am, context getting wiped mid-session and Claude forgetting critical rules.</p>
<p>So I stopped and asked: <strong>what if the AI agent had guardrails that enforced discipline automatically — and measured themselves?</strong></p>
<p>Over the past few weeks, I've built a hook system for Claude Code inside our fintech monorepo. Not a few scripts stitched together — a full lifecycle of pre-checks, post-validations, audit trails, context recovery, and a metrics layer that snapshots progress daily.</p>
<p>Here's what it looks like, what each hook does, and what the numbers are telling me.</p>
<hr />
<h2>What Are Claude Code Hooks?</h2>
<p>Think of hooks like airport security checkpoints for your AI agent.</p>
<p>Before Claude runs a bash command → a <strong>pre-tool hook</strong> inspects it. After Claude edits a file → a <strong>post-tool hook</strong> validates it. When Claude's context gets compacted (memory reset) → a <strong>compact hook</strong> saves and restores critical rules.</p>
<p>Every hook runs automatically. Zero manual intervention. Claude doesn't even know it's being watched — it just gets blocked or warned when it tries something it shouldn't.</p>
<hr />
<h2>The Hook Categories (and Why Each One Exists)</h2>
<h3>🛡️ 1. Safety Hooks — The Seatbelts</h3>
<p><strong>Problem:</strong> AI agents can run any bash command. Including destructive ones.</p>
<p><strong>Hook:</strong> <code>pre-tool-bash</code> — intercepts every bash command before execution and blocks patterns like:</p>
<pre><code class="language-plaintext">→ rm -rf /           ← blocked
→ DROP DATABASE       ← blocked
→ git push --force main  ← blocked
→ redis-cli FLUSHALL     ← blocked
</code></pre>
<p><strong>Real-world analogy:</strong> It's like a car that physically won't start if the seatbelt isn't on. You don't rely on the driver remembering — the system enforces it.</p>
<p><strong>Impact:</strong> Zero destructive commands have gotten through. Ever.</p>
<hr />
<h3>🧪 2. TDD Enforcement — The Bouncer</h3>
<p><strong>Problem:</strong> It's tempting to write production code first and "add tests later." With AI agents, this temptation is 10x worse — Claude can generate code so fast that tests become an afterthought.</p>
<p><strong>Hook:</strong> <code>pre-tool-tdd</code> — blocks any write to a <code>.ts</code> production file if the corresponding <code>.spec.ts</code> test file doesn't exist yet.</p>
<pre><code class="language-plaintext">Claude tries to write: apps/smf-core/src/services/ledger.service.ts

Hook checks: does ledger.service.spec.ts exist?
  → No? BLOCKED. Exit code 2. Write rejected.
  → Yes? Proceed.
</code></pre>
<p>Smart enough to exempt files that don't need tests — type definitions, enums, constants, DTOs, barrel exports.</p>
<p><strong>Real-world analogy:</strong> A bouncer at a club checking your ID. No test file = no entry. Doesn't matter how good the code looks.</p>
<p><strong>Current metric:</strong> 100% TDD compliance. Every production file has its test. Not because I'm disciplined — because the hook won't let me skip it.</p>
<hr />
<h3>🔍 3. Auto-Typecheck — The Spell Checker</h3>
<p><strong>Problem:</strong> TypeScript errors compound fast in a monorepo. One bad type change ripples across 6 packages. By the time you notice, you've built 3 more features on a broken foundation.</p>
<p><strong>Hook:</strong> <code>post-tool-typecheck</code> — runs a scoped typecheck on the affected workspace immediately after every <code>.ts</code> file edit.</p>
<pre><code class="language-plaintext">Claude edits: apps/auth-service/src/auth.controller.ts

Hook triggers: pnpm turbo run typecheck --filter=auth-service
  → Pass? Silent. Continue working.
  → Fail? Warning with exact error. Fix before moving on.
</code></pre>
<p><strong>Real-world analogy:</strong> Like spell-check highlighting errors as you type — not waiting until you hit "submit."</p>
<p><strong>Why it matters:</strong> Catches type regressions within seconds of introduction, not hours later during a build.</p>
<hr />
<h3>🎯 4. Agent Model Governance — The Right Specialist for the Right Job</h3>
<p><strong>Problem:</strong> Our monorepo uses multiple Claude agents — Haiku for lightweight services (card-processor, auth), Sonnet for complex business logic (smf-core, integration tests), Opus for final PM review. Spawning the wrong model wastes tokens and time.</p>
<p><strong>Hook:</strong> <code>pre-tool-agent</code> — validates that each spawned agent uses the expected model tier.</p>
<pre><code class="language-plaintext">Spawning: card-processor agent
Expected model: haiku
Actual model: sonnet
  → WARNING: Model mismatch detected. Consider using haiku.
</code></pre>
<p><strong>Real-world analogy:</strong> Like a hospital assigning the right specialist to the right procedure. You don't send the chief surgeon to check a blood pressure reading.</p>
<p><strong>Current metric:</strong> 57.1% model match rate. Yes, that's low — and the hook is exactly how I know it's low. Without measurement, I'd have assumed it was fine. Now I'm actively improving it.</p>
<hr />
<h3>🧠 5. Context Recovery — Saving Your Game Before the Boss Fight</h3>
<p><strong>Problem:</strong> Claude Code compacts context when conversations get long. When it compacts, it can forget critical rules — like "always write tests first" or "never modify shared-types directly."</p>
<p><strong>Hooks:</strong> <code>pre-compact</code> + <code>post-compact-inject</code> working as a pair.</p>
<pre><code class="language-plaintext">BEFORE compaction:
  → pre-compact takes a snapshot:
    - Current status files
    - Git state
    - 5 critical rules

AFTER compaction:
  → post-compact-inject restores:
    - All 5 critical rules as additionalContext
    - The pre-compact snapshot for state recovery
</code></pre>
<p>The 5 critical rules that survive every compaction:</p>
<ol>
<li><p>TDD: failing test first, then implementation</p>
</li>
<li><p>Typecheck after every edit</p>
</li>
<li><p>Never modify shared-types directly</p>
</li>
<li><p>Modern Treasury metadata must include <code>smf_type</code></p>
</li>
<li><p>Phase 3 review is mandatory before commit</p>
</li>
</ol>
<p><strong>Real-world analogy:</strong> Like saving your game before a boss fight. If you die (context reset), you reload from the save point — not from the beginning.</p>
<p><strong>Current metric:</strong> 6 compaction events survived with zero rule amnesia.</p>
<hr />
<h3>📝 6. Audit Trail — The Black Box Recorder</h3>
<p><strong>Problem:</strong> When something goes wrong in a long AI session, you need to trace what happened. "What commands did Claude run? What files did it touch? Which skills did it use?"</p>
<p><strong>Hooks:</strong> Three loggers running in parallel:</p>
<pre><code class="language-plaintext">command-logger  → logs every bash command (rolling 200 entries)
skill-logger    → logs every skill invocation (rolling 500 entries)
subagent-logger → logs every agent spawn (model, type, description)
write-audit     → logs every file write with timestamp
</code></pre>
<p>Plus a shared error trap (<code>_err-trap</code>) that captures hook failures with timestamp, exit code, and exact line number.</p>
<p><strong>Real-world analogy:</strong> A flight data recorder (black box). You hope you never need it. But when you do, it tells you exactly what happened.</p>
<p><strong>Current metric:</strong> 227 file operations logged. 200 commands tracked. 10 skill/agent invocations recorded. Every single one traceable.</p>
<hr />
<h3>🗼 7. Orchestration Hooks — Air Traffic Control</h3>
<p><strong>Problem:</strong> In a multi-agent monorepo, agents can block each other. One agent hits an unresolvable issue → the whole pipeline needs to halt. Another agent finishes → the next phase should begin.</p>
<p><strong>Hook:</strong> <code>subagent-stop</code> — runs when any sub-agent completes:</p>
<pre><code class="language-plaintext">Agent finishes → hook checks status files:
  → PROJECT_BLOCKED.md found? HALT EVERYTHING. Surface to human.
  → BLOCKED.md found? Log it. Attempt retry.
  → FIXED.md found? All clear. Advance to next phase.
</code></pre>
<p><strong>Real-world analogy:</strong> Air traffic control. Every plane (agent) reports its status on landing. ATC (orchestrator) decides what takes off next.</p>
<hr />
<h2>The Metrics Layer — Hooks That Measure Themselves</h2>
<p>Here's where it gets interesting.</p>
<p>The hooks don't just enforce rules — they generate structured telemetry. Every blocked command, every TDD violation, every model mismatch, every compaction event gets logged as a JSONL entry with timestamps.</p>
<p>Then a <code>/metrics</code> skill reads all that telemetry, computes 6 core metrics, compares against the previous day's snapshot, and shows trends:</p>
<pre><code class="language-plaintext">┌──────────────────────┬─────────
│ Metric               │ Today   │ Trend           │
├──────────────────────┼─────────
│ Hook Reliability     │ 98.6%   │   →            │
│ TDD Compliance       │ 100%    │   →            │
│ Agent Model Match    │ 57.1%   │   ↑             │
│ Typecheck Pass Rate  │ pending │   —             │
│ Review First-Pass    │ pending │   —             │
│ Compaction Events    │ 6/week  │   →            │
└──────────────────────┴─────────
</code></pre>
<p>Daily snapshots get saved to <code>.claude/metrics/snapshots/</code> — one JSON file per day. Over time, this builds a <strong>progress timeline</strong> showing exactly where discipline is improving and where it's slipping.</p>
<h3>What the Numbers Are Telling Me</h3>
<ul>
<li><p><strong>98.6% hook reliability</strong> across 427+ automated runs — 4 errors total, all from the agent model hook during early calibration. The system is stable.</p>
</li>
<li><p><strong>100% TDD compliance</strong> — not a single production file exists without its test. The bouncer works.</p>
</li>
<li><p><strong>57.1% agent model match</strong> — this is the weakest spot, and I only know because the hook is measuring it. Active improvement area.</p>
</li>
<li><p><strong>6 compaction survivals</strong> — context got wiped 6 times, and all 5 critical rules were restored every time. Zero rule amnesia.</p>
</li>
</ul>
<h3>Supporting Skills in the Ecosystem</h3>
<p>The metrics don't just sit in JSON files. They feed into a broader analysis ecosystem:</p>
<ul>
<li><p><code>/health-check</code> — runs at session start, reports hook errors from the last 7 days</p>
</li>
<li><p><code>/reflect</code> — analyzes command logs and skill usage to suggest new automation rules</p>
</li>
<li><p><code>/retro</code> — weekly retrospective with week-over-week trend comparison</p>
</li>
<li><p><code>analyze-patterns.sh</code> — detects repeated commands (3+ times) and suggests turning them into skills</p>
</li>
</ul>
<p>It's a feedback loop: hooks enforce → metrics measure → skills analyze → rules improve → hooks enforce better.</p>
<hr />
<h2>The Flywheel Effect</h2>
<p>Here's the mental model I keep coming back to:</p>
<pre><code class="language-plaintext">     ┌──────────────┐
     │   Hooks       │ ← enforce rules automatically
     │   (guardrails)│
     └──────┬───────┘
            │ generate
            ▼
     ┌──────────────┐
     │   Metrics     │ ← structured telemetry
     │   (JSONL)     │
     └──────┬───────┘
            │ feed into
            ▼
     ┌──────────────┐
     │   Skills      │ ← /metrics, /reflect, /retro
     │   (analysis)  │
     └──────┬───────┘
            │ suggest
            ▼
     ┌──────────────┐
     │   Rules       │ ← CLAUDE.md, new hooks
     │   (improved)  │
     └──────┬───────┘
            │ enforced by
            ▼
     ┌──────────────┐
     │   Hooks       │ ← cycle repeats, tighter each time
     └──────────────┘
</code></pre>
<p>Every cycle, the system gets tighter. The agent gets more disciplined. The code gets more reliable. And I have the numbers to prove it's actually happening.</p>
<hr />
<h2>What I'd Tell Someone Starting This Today</h2>
<ul>
<li><p><strong>Start with safety hooks.</strong> Block destructive commands before anything else. It takes 10 minutes and saves you from catastrophic mistakes.</p>
</li>
<li><p><strong>Add TDD enforcement early.</strong> Once you have 500 files without tests, it's too late. The bouncer is easiest to install when the club is empty.</p>
</li>
<li><p><strong>Measure everything, even the embarrassing numbers.</strong> My 57% model match rate isn't pretty. But I can only improve what I can see.</p>
</li>
<li><p><strong>Build the compact recovery hooks.</strong> Context loss is the silent killer of long AI sessions. Your agent will forget your most important rules at the worst possible time.</p>
</li>
<li><p><strong>Close the feedback loop.</strong> Hooks without metrics are just guardrails. Hooks WITH metrics are a self-improving system.</p>
</li>
</ul>
<hr />
<p>AI coding agents are powerful. But power without guardrails is just chaos with better autocomplete.</p>
<p>Build the guardrails. Measure the guardrails. Let the guardrails measure themselves.</p>
<p>That's how you go from "AI writes code for me" to "AI writes code the way I want it to."</p>
<p>🔧 Building this at OrbitxPay — where we're wiring traditional banking to onchain infrastructure. If you're building with AI agents in production, I'd love to hear what guardrails you've put in place.</p>
<p>#ClaudeCode #AIAgents #DevTooling #BuildingInPublic #EngineeringLeadership #Fintech</p>
]]></content:encoded></item><item><title><![CDATA[MongoDB ObjectID: anatomy of a distributed identifier]]></title><description><![CDATA[MongoDB's default ObjectID exists because distributed databases cannot afford the coordination cost of sequential integers. Rather than relying on a single authority to hand out the next number — a bo]]></description><link>https://tech.sedhu.me/mongodb-objectid-anatomy-of-a-distributed-identifier</link><guid isPermaLink="true">https://tech.sedhu.me/mongodb-objectid-anatomy-of-a-distributed-identifier</guid><dc:creator><![CDATA[Sedhu]]></dc:creator><pubDate>Wed, 04 Mar 2026 12:48:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/5ffd78174084696c9d22a46f/4c301ea6-d6f6-4f90-8deb-c0da8f5906a6.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>MongoDB's default ObjectID exists because <strong>distributed databases cannot afford the coordination cost of sequential integers</strong>. Rather than relying on a single authority to hand out the next number — a bottleneck that throttles write throughput across every shard and replica — MongoDB drivers generate 12-byte ObjectIDs entirely client-side, producing globally unique identifiers without any cross-node communication. This design choice is foundational to MongoDB's horizontal scalability. The ObjectID encodes a timestamp, a per-process random value, and an incrementing counter into a compact 96-bit value that is roughly time-ordered, storage-efficient, and collision-resistant across billions of documents.</p>
<h2>Why auto-incrementing integers fail at scale</h2>
<p>Sequential integer IDs require a centralized counter — a single document or service that every node must consult before inserting. In a distributed MongoDB cluster, this creates three compounding problems. First, every insert needs a network round-trip to the counter authority, adding latency proportional to geographic distance. Second, that counter becomes a <strong>single point of failure</strong>: if it goes down, no node can insert anything. Third, concurrent access to the counter creates race conditions. MongoDB's own engineering blog illustrates this: two threads read the current value (41), both compute 42, and both attempt to store documents with the same ID — causing a duplicate key error or a lost write. Solving this with atomic <code>findAndModify</code> operations serializes all inserts through a single bottleneck, destroying throughput.</p>
<p>ObjectID sidesteps all of this. The MongoDB driver generates the ID on the application server before the insert ever reaches the database. No coordination, no contention, no central authority. Each client independently produces unique IDs, and insertion throughput scales linearly with the number of application servers.</p>
<h2>The 12-byte anatomy of an ObjectID</h2>
<p>An ObjectID is <strong>12 bytes rendered as a 24-character hexadecimal string</strong> — for example, <code>507f1f77bcf86cd799439011</code>. Since a 2018 specification revision (formalized for MongoDB 3.4+), those 12 bytes break down into three segments:</p>
<p><strong>Bytes 0–3: Unix timestamp (4 bytes).</strong> A 32-bit unsigned integer representing seconds since January 1, 1970 UTC, stored in big-endian byte order. This gives one-second resolution and extends to approximately the year 2106, avoiding the signed 32-bit overflow problem of 2038. Big-endian encoding is deliberate — it allows raw <code>memcmp</code> byte comparison to produce correct chronological ordering, which is critical for index efficiency.</p>
<p><strong>Bytes 4–8: Per-process random value (5 bytes).</strong> Generated once when the driver process starts and held constant for the lifetime of that process. This value does not need to be cryptographically secure; a standard PRNG seeded with OS entropy suffices. The 5-byte width provides <strong>~1.1 trillion</strong> possible values, making cross-process collisions statistically negligible.</p>
<p><strong>Bytes 9–11: Incrementing counter (3 bytes).</strong> Initialized to a random value at driver startup and incremented by one for each ObjectID generated. Stored in big-endian order. The 3-byte counter supports <strong>16,777,216 unique ObjectIDs per process per second</strong> before wrapping to zero.</p>
<p>The pre-3.4 format split bytes 4–8 differently: 3 bytes for an MD5 hash of the machine hostname plus 2 bytes for the process ID. MongoDB retired this layout because MD5 cannot be used on FIPS-compliant systems, virtual machines cloned from identical images produced identical machine hashes, and different language drivers implemented these fields inconsistently. Merging machine ID and process ID into a single 5-byte random value solved all three problems while maintaining uniqueness guarantees.</p>
<h2>Why ObjectID excels in distributed architectures</h2>
<p>The three-segment design creates a layered uniqueness guarantee without any distributed consensus. Documents created in different seconds differ in the timestamp. Documents from different processes in the same second differ in the random value. Documents from the same process in the same second differ in the counter. The only theoretical collision requires a single process to generate more than 16.7 million ObjectIDs within one second — an extreme edge case.</p>
<p>Beyond uniqueness, ObjectID provides <strong>roughly monotonic ordering</strong> because the leading bytes are a timestamp. Sorting by <code>_id</code> approximates sorting by creation time, and range queries on <code>_id</code> can efficiently select documents from a time window. The embedded timestamp also eliminates the need for a separate <code>createdAt</code> field in many applications — any ObjectID's creation time is extractable via <code>ObjectId.getTimestamp()</code> with one-second precision.</p>
<p>At <strong>12 bytes</strong>, ObjectID is more compact than a UUID's 16 bytes (33% smaller) or a string UUID's 36 bytes. Since every MongoDB document carries an <code>_id</code> field with a mandatory unique index, this size difference compounds across millions of documents — affecting index memory footprint, disk usage, and WiredTiger cache efficiency.</p>
<p>Two caveats apply. ObjectIDs created within the same second from different machines have no guaranteed ordering, and clients with skewed system clocks can produce out-of-order timestamps.</p>
<h2>Performance costs of random IDs versus monotonic ones</h2>
<p>The performance gap between ObjectID and random identifiers like UUIDv4 stems from B-tree index mechanics in WiredTiger, MongoDB's default storage engine. ObjectID's roughly monotonic nature means new entries append to the <strong>right edge of the B-tree</strong>, keeping the hot working set confined to a few rightmost leaf pages. Only these pages need to reside in the WiredTiger cache for write-heavy workloads.</p>
<p>Random UUIDs scatter inserts across the entire B-tree. Each random insert may land on a page not currently in cache, forcing a disk read. When a leaf page fills, it splits — and random UUIDs cause <strong>500× more page splits</strong> than sequential IDs (roughly 5,000–10,000 splits per million records versus 10–20). Each split triggers cascading rebalancing up the tree, producing <strong>5–10× write amplification</strong> per logical insert.</p>
<p>Benchmarks confirm the theory. A Go benchmark against MongoDB 6.0.5 with 1 million batched inserts (batch size 10,000) measured ObjectID at <strong>3.32 seconds</strong>, ULID at 3.52 seconds, and UUID at <strong>5.16 seconds — 55% slower</strong>. The gap widens dramatically at scale: inserting 10 million documents into a collection already containing 10 million took ObjectID about 32 seconds versus over 63 seconds for UUID. Separate benchmarks report UUIDs sustaining roughly <strong>2,000 inserts per second</strong> at 10–20 million documents where ObjectIDs sustain 7,500 — a 3.75× throughput difference.</p>
<p>Index size also matters. ObjectID occupies 12 bytes per key; a binary UUID takes 16 bytes (33% more); a string UUID takes 36 bytes (3× more). With MongoDB's typical 4 KB index pages, ObjectID fits approximately <strong>317 entries per page</strong> versus fewer for larger key types, directly affecting how much of the index the cache can hold.</p>
<h2>Custom IDs are fully supported and sometimes preferable</h2>
<p>MongoDB's <code>_id</code> field accepts <strong>any BSON data type except arrays, regex, or undefined</strong> — strings, integers, UUIDs, dates, embedded documents, and more. To use a custom ID, simply include <code>_id</code> in the document at insert time. If omitted, the driver auto-generates an ObjectID.</p>
<p>The strongest case for custom IDs is <strong>natural keys that will never change</strong>. Using an email address, username, or external system identifier as <code>_id</code> eliminates the need for a separate unique index on that field, saving both storage and write overhead. MongoDB's documentation explicitly recommends this: "Use a natural unique identifier, if available. This saves space and avoids additional indexes."</p>
<p>Compound <code>_id</code> values — embedded documents like <code>{ region: "US", order: 12345 }</code> — work well for data with natural composite keys. In application code, the implementation is straightforward across drivers:</p>
<pre><code class="language-javascript">// Node.js
await collection.insertOne({ _id: "user@example.com", name: "Alice" });

// Mongoose schema with custom _id
const schema = new mongoose.Schema({
  _id: { type: String, required: true },
  name: String
});
</code></pre>
<pre><code class="language-python"># PyMongo
collection.insert_one({ '_id': 'user@example.com', 'name': 'Alice' })
</code></pre>
<p>MongoDB's Node.js driver also supports a <code>pkFactory</code> option that auto-generates custom IDs when none is provided, enabling application-wide ID strategies without modifying every insert call.</p>
<h2>Navigating the trade-offs between ObjectID and alternatives</h2>
<p>The decision framework rests on five dimensions, each favoring different ID strategies.</p>
<p><strong>Write performance and scalability</strong> strongly favor monotonic IDs. ObjectID and time-ordered alternatives (UUIDv7, ULID, KSUID) maintain right-edge B-tree appends. Random UUIDs (v4) degrade catastrophically beyond 10 million documents. For write-heavy workloads at scale, this is often the deciding factor.</p>
<p><strong>Privacy and security</strong> favor random UUIDs. ObjectID's first four bytes encode the creation timestamp in a trivially decodable format. Exposing ObjectIDs in URLs or APIs reveals when every document was created, enabling enumeration attacks and leaking usage patterns. UUIDv4 reveals nothing. UUIDv7 encodes time but in a less immediately obvious format.</p>
<p><strong>Portability across databases</strong> favors UUIDs. ObjectID is MongoDB-specific. Applications that might migrate to PostgreSQL, MySQL, or another database benefit from the universal UUID standard. MongoDB's own blog acknowledges this: "Some businesses may be reluctant to link their application logic to an identifier generated by a specific database product."</p>
<p><strong>Storage efficiency</strong> favors ObjectID (12 bytes) over binary UUID (16 bytes) and dramatically over string UUID (36 bytes). For collections with hundreds of millions of documents, the 33% index size reduction versus binary UUID translates to meaningful memory and disk savings.</p>
<p><strong>Sharding behavior</strong> requires special attention regardless of ID type. Monotonically increasing IDs (including ObjectID) used directly as a shard key route all inserts to a single chunk, creating hotspots. The solution is hashed sharding (<code>{ _id: "hashed" }</code>) or compound shard keys. Random UUIDs distribute writes evenly but sacrifice range query efficiency.</p>
<h2>Conclusion</h2>
<p>ObjectID is MongoDB's default for a reason: it delivers <strong>globally unique, roughly time-ordered, compact identifiers without any distributed coordination</strong> — the exact properties a horizontally scalable database needs. Its 12-byte structure packs a timestamp, process-unique random value, and counter into a format optimized for B-tree index performance and <code>memcmp</code> sorting.</p>
<p>Custom IDs make sense in specific scenarios — natural keys that eliminate redundant indexes, UUIDs required for cross-database portability, or random IDs needed to prevent timestamp leakage. The critical insight is that <strong>ID ordering matters more than ID type</strong>: any time-ordered identifier (ObjectID, UUIDv7, ULID) will dramatically outperform random ones at scale due to B-tree mechanics. Applications choosing custom IDs should prioritize monotonic or semi-monotonic strategies, store UUIDs as binary rather than strings, and test write performance at target collection sizes before committing to an ID scheme.</p>
]]></content:encoded></item><item><title><![CDATA[Stop Chasing Your Team: How to Build High-Agency Engineering Cultures]]></title><description><![CDATA[From Follow-up Machine to High-Agency Team: A Guide to Real Ownership
Ever feel like your main job is just being a human reminder? You spend your day asking for updates, checking if a PR was reviewed, or wondering why a task hasn't moved in three day...]]></description><link>https://tech.sedhu.me/stop-chasing-your-team-how-to-build-high-agency-engineering-cultures</link><guid isPermaLink="true">https://tech.sedhu.me/stop-chasing-your-team-how-to-build-high-agency-engineering-cultures</guid><category><![CDATA[team]]></category><category><![CDATA[teams]]></category><category><![CDATA[ownership]]></category><category><![CDATA[agency]]></category><dc:creator><![CDATA[Sedhu]]></dc:creator><pubDate>Tue, 03 Feb 2026 04:06:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/V7Q94jc04wQ/upload/0db0d516e450ca70fa974b2b11592496.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-from-follow-up-machine-to-high-agency-team-a-guide-to-real-ownership">From Follow-up Machine to High-Agency Team: A Guide to Real Ownership</h1>
<p>Ever feel like your main job is just being a human reminder? You spend your day asking for updates, checking if a PR was reviewed, or wondering why a task hasn't moved in three days. It’s exhausting for you and, honestly, it’s not great for the team either. 😫</p>
<p>Building a team that "self-delivers" isn't about hiring superheroes. It’s about shifting the culture from "doing tasks" to "owning outcomes." Let’s look at how to stop the constant follow-ups and build a high-agency environment.</p>
<h2 id="heading-understanding-the-agency-gap">Understanding the Agency Gap</h2>
<p>For a junior engineer or someone new to the field, work often feels like a checklist. You get a ticket, you write the code, you move it to "Testing," and you're done. This is <strong>low agency</strong>. The mindset is: "I did my part, now it's someone else's problem." 🛑</p>
<p><strong>High agency</strong> is the exact opposite. It’s the refusal to let a project stall just because a hurdle appeared. If a high-agency engineer sees a delay, they don't just wait; they find a way around it or pull the right people together to fix it. They don't just own the code; they own the result.</p>
<h3 id="heading-the-not-my-problem-trap">The "Not My Problem" Trap</h3>
<p>We’ve all heard it: "It’s pending on DevOps" or "I’m waiting for the designer." When people say this and then sit idle, the momentum dies. To fix this, you have to reward the behavior of <em>clearing</em> blockers, not just finishing tickets.</p>
<h2 id="heading-how-to-build-a-culture-of-ownership">How to Build a Culture of Ownership</h2>
<p>Ownership can't be forced, but it can be engineered. You have to create an environment where taking initiative is the path of least resistance. 🛠️</p>
<p>1. <strong>Define the 'Definition of Done' clearly.</strong> Done doesn't mean the code is pushed. Done means it's in the hands of the user and working as expected.<br />2. <strong>Stop the micro-management.</strong> If you follow up every hour, the team learns they don't need to track their own progress because you'll do it for them. Step back and see if they pick up the slack.<br />3. <strong>Give them the 'Why,' not just the 'How.'</strong> When engineers understand the business impact of a feature, they’re more likely to care about it actually crossing the finish line.</p>
<h2 id="heading-removing-the-hurdles-the-process-fix">Removing the Hurdles (The Process Fix)</h2>
<p>Sometimes, the team isn't lazy; the process is just broken. If your workflow requires five manual approvals and a blood sacrifice to deploy, people will naturally lose interest. 📉</p>
<p><strong>Manual approvals are momentum killers.</strong> If someone has to wait for a manager to click a button, they'll check out and start something else. By the time the approval comes, they've lost their flow.</p>
<p>- <strong>Automate everything possible.</strong> Use automated testing and CI/CD to replace manual sign-offs.<br />- <strong>Empower peer reviews.</strong> Let the team trust each other rather than waiting for a single "gatekeeper."<br />- <strong>Reduce handoffs.</strong> Every time a task moves from one person to another, there's a 50% chance it gets dropped. Keep the owner the same from start to finish if you can.</p>
<p><strong>Spotting Burnout vs. Boredom</strong></p>
<p>When a high-performer starts saying "I've done my stuff, it's pending elsewhere," it might be a red flag for burnout. Burnout doesn't always look like exhaustion; sometimes it looks like <strong>cynicism or apathy.</strong> 🚩</p>
<p>If someone feels like their effort doesn't matter because the system is too slow, they stop trying. They check out emotionally to protect themselves from the frustration of being blocked.</p>
<p>How to spot it:  </p>
<p>- <strong>The 'Silent' Developer:</strong> Someone who used to be vocal in meetings but now just nods.<br />- <strong>The Bare Minimum:</strong> They do exactly what's on the ticket, but nothing more—even if there's an obvious bug right next to their change.<br />- <strong>Frequent Friction:</strong> They seem annoyed by small requests or process changes.</p>
<h2 id="heading-key-takeaways-for-leaders">Key Takeaways for Leaders</h2>
<p>To fix a team that needs constant follow-ups, you have to stop being the one who follows up. Shift the responsibility.</p>
<p>- <strong>Make work visible.</strong> Use a board where blockers are glaringly obvious so the team feels the pressure to clear them, not you.<br />- <strong>Kill the wait times.</strong> Audit your workflow this week. Find one manual approval and delete it. See what happens.<br />- <strong>Celebrate the 'Finishers.'</strong> Don't just praise the person who wrote the most code. Praise the person who chased down the stakeholder to get the final sign-off. 🏆</p>
<p>High agency is contagious. Once a few people start taking real ownership, the rest of the team usually steps up to match that energy.</p>
<p>#Leadership #Engineering #Productivity</p>
<p>#EngineeringLeadership #HighAgency #TeamCulture</p>
]]></content:encoded></item><item><title><![CDATA[Viewstamped Replication]]></title><description><![CDATA[Introduction
Imagine you and your two best friends are keeping score in a video game. One friend is the "scorekeeper" and announces all the points. But what if the scorekeeper's controller breaks mid-game? You need a way to agree on the score without...]]></description><link>https://tech.sedhu.me/viewstamped-replication</link><guid isPermaLink="true">https://tech.sedhu.me/viewstamped-replication</guid><category><![CDATA[vsr]]></category><dc:creator><![CDATA[Sedhu]]></dc:creator><pubDate>Thu, 13 Nov 2025 06:13:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/3y1zF4hIPCg/upload/cf8d71857b2cffcfcd109f049a4c7e54.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Imagine you and your two best friends are keeping score in a video game. One friend is the "scorekeeper" and announces all the points. But what if the scorekeeper's controller breaks mid-game? You need a way to agree on the score without them.</p>
<p>Viewstamped Replication is like having a protocol: "If the scorekeeper fails, we pick the next person in a predetermined order—no arguing." You also write down every score on paper (all three of you), so even if someone forgets, you can check the paper and catch up.</p>
<hr />
<h2 id="heading-abstract">Abstract</h2>
<p>Viewstamped Replication (VSR) is a consensus algorithm that keeps replicated systems consistent when nodes fail. Here's how it works:</p>
<h3 id="heading-key-concepts">Key Concepts</h3>
<ol>
<li><p><strong>Normal Operation:</strong> A designated "primary" node receives requests from clients, stamps them with a view number (like a logical clock), and sends them to backup replicas. The primary waits for acknowledgment from a quorum (majority) before committing the operation.</p>
</li>
<li><p><strong>View Change (Failover):</strong> If the primary becomes unresponsive (detected via timeout), the system automatically transitions to a new view. The next replica in a deterministic round-robin order becomes the new primary—no elections, no voting.</p>
</li>
<li><p><strong>Quorum Intersection:</strong> Every committed operation is known by at least f+1 replicas in a 2f+1 system. When a new primary takes over, it queries at least f+1 replicas to find the latest committed state, ensuring no data loss.</p>
</li>
<li><p><strong>View Numbers:</strong> Operations are tagged with view numbers, ensuring ordering even across view changes. If two operations have the same sequence number, the one from a higher view number "wins."</p>
</li>
</ol>
<p>The beauty: it's <strong>simpler than Raft</strong> (no split-vote elections) and <strong>faster</strong> (deterministic leader selection, parallel replication).</p>
<h3 id="heading-vsr-normal-operation-flow">VSR Normal Operation Flow</h3>
<pre><code class="lang-mermaid">sequenceDiagram
    participant Client
    participant Primary
    participant Replica1
    participant Replica2

    Client-&gt;&gt;Primary: Request (view=5, seq=100)
    Primary-&gt;&gt;Primary: Stamp request
    Primary-&gt;&gt;Replica1: PREPARE (view=5, seq=100, op)
    Primary-&gt;&gt;Replica2: PREPARE (view=5, seq=100, op)
    Replica1-&gt;&gt;Replica1: Log entry
    Replica2-&gt;&gt;Replica2: Log entry
    Replica1-&gt;&gt;Primary: PREPAREOK
    Replica2-&gt;&gt;Primary: PREPAREOK
    Primary-&gt;&gt;Primary: Quorum acknowledged
    Primary-&gt;&gt;Replica1: COMMIT (view=5, seq=100)
    Primary-&gt;&gt;Replica2: COMMIT (view=5, seq=100)
    Replica1-&gt;&gt;Replica1: Execute operation
    Replica2-&gt;&gt;Replica2: Execute operation
    Primary-&gt;&gt;Client: ACK (result)
</code></pre>
<h3 id="heading-vsr-view-change-flow">VSR View Change Flow</h3>
<pre><code class="lang-mermaid">sequenceDiagram
    participant OldPrimary as Old Primary&lt;br/&gt;(Failed)
    participant Replica1
    participant Replica2
    participant NewPrimary as New Primary&lt;br/&gt;(Replica1)

    Replica2-&gt;&gt;Replica2: Heartbeat timeout
    Replica2-&gt;&gt;Replica1: START-VIEW-CHANGE (view=6)
    Replica2-&gt;&gt;Replica2: START-VIEW-CHANGE (view=6)
    Replica1-&gt;&gt;Replica1: Receive START-VIEW-CHANGE
    Replica1-&gt;&gt;Replica1: Advance view to 6
    Replica2-&gt;&gt;Replica1: DO-VIEW-CHANGE (view=6, log_info)
    Replica2-&gt;&gt;Replica2: DO-VIEW-CHANGE (view=6, log_info)
    Replica1-&gt;&gt;Replica1: Quorum collected
    Replica1-&gt;&gt;Replica2: START-VIEW (view=6, log)
    Replica1-&gt;&gt;Replica2: Become New Primary
    Replica2-&gt;&gt;Replica2: Accept view 6
    NewPrimary-&gt;&gt;Replica2: Normal operation resumes
</code></pre>
<hr />
<h2 id="heading-architecture">Architecture</h2>
<p>VSR achieves strong safety guarantees with superior performance characteristics compared to Multi-Paxos and Raft. Here's the technical depth:</p>
<h3 id="heading-key-architectural-differences-from-raft">Key Architectural Differences from Raft</h3>
<p><strong>Deterministic Leader Selection</strong></p>
<ul>
<li>Unlike Raft's randomized election timeouts (which introduce latency variance), VSR pre-determines the next primary as <code>(current_primary_index + 1) mod cluster_size</code>. This eliminates split-vote scenarios and achieves sub-millisecond failover.</li>
</ul>
<p><strong>Passive vs. Active Replication</strong></p>
<ul>
<li>Raft employs active replication—all replicas independently execute operations. VSR uses passive replication—only the primary executes operations, reducing state machine redundancy overhead. Backups maintain logs purely for recovery.</li>
</ul>
<p><strong>Recovery Protocol</strong></p>
<ul>
<li>VSR includes a built-in recovery protocol for crashed nodes rejoining the cluster. A recovering node requests state from at least f+1 replicas (including the current primary) and applies the latest known committed state. This decouples recovery from durable storage guarantees, unlike Raft which assumes perfect storage.</li>
</ul>
<p><strong>View Change Protocol</strong></p>
<ul>
<li>When a replica detects primary failure (via heartbeat timeout or explicit view-change messages), it advances its view number and broadcasts <code>START-VIEW-CHANGE</code>. When a majority of <code>DO-VIEW-CHANGE</code> messages is collected, the designated new primary sends <code>START-VIEW</code> with the highest-numbered log from the quorum, ensuring linearizability.</li>
</ul>
<h3 id="heading-comparison-table-vsr-vs-raft-vs-paxos">Comparison Table: VSR vs Raft vs Paxos</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Aspect</td><td>Raft</td><td>Viewstamped Replication</td><td>Paxos (Multi-Paxos)</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Leader Election</strong></td><td>Randomized timeouts, split-vote problem</td><td>Deterministic modulo, no split votes</td><td>Two-phase with proposer election</td></tr>
<tr>
<td><strong>Failover Latency</strong></td><td>Variable (election timeout + message delays)</td><td>Predictable (heartbeat timeout + view change)</td><td>Higher (Prepare phase + Accept phase)</td></tr>
<tr>
<td><strong>Log Replication</strong></td><td>Active (all replicas execute)</td><td>Passive (primary only executes)</td><td>Active (all replicas may execute)</td></tr>
<tr>
<td><strong>Recovery</strong></td><td>Depends on perfect durable storage</td><td>Independent recovery protocol (Merkle trees)</td><td>Depends on learner state</td></tr>
<tr>
<td><strong>Availability</strong></td><td>Lower (leader must have pristine log)</td><td>Higher (quorum repair via Merkle trees)</td><td>Moderate (learner-based recovery)</td></tr>
<tr>
<td><strong>Throughput</strong></td><td>Good</td><td>Higher (batching, pipelining, lower execution overhead)</td><td>Good (but more complex)</td></tr>
<tr>
<td><strong>Complexity</strong></td><td>Medium</td><td>Medium-High</td><td>High</td></tr>
<tr>
<td><strong>Production Systems</strong></td><td>etcd, Consul, CockroachDB</td><td>Google Spanner, HDFS JournalNodes, <strong>TigerBeetle</strong></td><td>Google Chubby, Apache ZooKeeper</td></tr>
<tr>
<td><strong>Message Complexity</strong></td><td>O(n) per operation</td><td>O(n) per operation</td><td>O(n²) in worst case</td></tr>
<tr>
<td><strong>Byzantine Tolerance</strong></td><td>No</td><td>No</td><td>Yes (with BFT variant)</td></tr>
</tbody>
</table>
</div><h3 id="heading-quorum-intersection-property">Quorum Intersection Property</h3>
<pre><code class="lang-mermaid">graph TD
    A["Cluster: 5 Nodes&lt;br/&gt;(N=5, f=2)"] --&gt; B["Quorum Size: 3"]
    B --&gt; C["Any two quorums&lt;br/&gt;must overlap by ≥1"]
    C --&gt; D["Primary commits&lt;br/&gt;with 3 replicas"]
    D --&gt; E["New primary queries&lt;br/&gt;f+1=3 replicas"]
    E --&gt; F["Guaranteed to find&lt;br/&gt;latest committed state"]
</code></pre>
<h3 id="heading-vsr-state-machine-architecture">VSR State Machine Architecture</h3>
<pre><code class="lang-mermaid">stateDiagram-v2
    [*] --&gt; NormalMode

    NormalMode --&gt; ViewChange: Primary fails&lt;br/&gt;or heartbeat timeout
    NormalMode --&gt; NormalMode: Client requests&lt;br/&gt;PREPARE + COMMIT

    ViewChange --&gt; DetermineLead: Elect new primary&lt;br/&gt;via round-robin
    DetermineLead --&gt; SyncState: New primary queries&lt;br/&gt;f+1 replicas
    SyncState --&gt; NormalMode: Log synchronized&lt;br/&gt;Ready to serve

    ViewChange --&gt; ViewChange: If new primary fails
</code></pre>
<hr />
<h2 id="heading-how-vsr-powers-tigerbeetles-1000x-throughput">How VSR Powers TigerBeetle's 1000x Throughput</h2>
<h3 id="heading-throughput-optimization-flow">Throughput Optimization Flow</h3>
<pre><code class="lang-mermaid">graph LR
    A["Transaction Batch&lt;br/&gt;8,000 txns"] --&gt; B["Stamp with&lt;br/&gt;View Number&lt;br/&gt;O1"]
    B --&gt; C["Send to Quorum&lt;br/&gt;in Parallel"]
    C --&gt; D["Replicas Log&lt;br/&gt;in Parallel"]
    D --&gt; E["Quorum Ack&lt;br/&gt;f+1 nodes"]
    E --&gt; F["Execute Serially&lt;br/&gt;on State Machine"]
    F --&gt; G["Commit + Response&lt;br/&gt;to Clients"]

    style A fill:#e1f5ff
    style G fill:#c8e6c9
</code></pre>
<h3 id="heading-why-vsr-wins-for-financial-ledgers">Why VSR Wins for Financial Ledgers</h3>
<ol>
<li><p><strong>Batching Efficiency:</strong> TigerBeetle batches up to 8,000 transactions per batch. VSR's view stamping (a lightweight logical clock) costs almost nothing—just an integer increment and replication.</p>
</li>
<li><p><strong>Normal Path Optimization:</strong> In the steady state, VSR consensus isn't engaged at all—replicas simply append to their logs in parallel and execute serially on the replicated state machine. This bypasses heavy consensus coordination.</p>
</li>
<li><p><strong>Deterministic Failover:</strong> No leader election storms. When the primary fails, the next replica knows immediately it's next (round-robin). No heartbeat storms, no cascade of election messages—just clean handoff.</p>
</li>
<li><p><strong>Quorum Replication:</strong> Requiring f+1 acknowledgments before commit (not 2f+1) means TigerBeetle replicates to a majority faster than protocols requiring unanimous replication, reducing tail latency.</p>
</li>
<li><p><strong>Immutable Ledger Integration:</strong> VSR's append-only nature aligns perfectly with financial ledger semantics. Every transfer is stamped with a view number that serves as a global ordering—this becomes your audit trail automatically.</p>
</li>
</ol>
<h3 id="heading-performance-comparison">Performance Comparison</h3>
<pre><code class="lang-mermaid">graph LR
    A["VSR&lt;br/&gt;Batching"] --&gt;|8K txns| B["1 Stamp&lt;br/&gt;1 Replication&lt;br/&gt;1 Execution"]
    C["Raft&lt;br/&gt;Per-txn"] --&gt;|1 txn| D["1 Election check&lt;br/&gt;1 Replication&lt;br/&gt;1 Execution"]

    B --&gt; E["~1000x more&lt;br/&gt;throughput"]
    D --&gt; E

    style E fill:#fff9c4
</code></pre>
<hr />
<h2 id="heading-key-advantages-of-vsr">Key Advantages of VSR</h2>
<h3 id="heading-safety-guarantees">Safety Guarantees</h3>
<pre><code class="lang-mermaid">graph TD
    A["Linearizability"] --&gt; B["Every operation appears&lt;br/&gt;atomic"]
    C["Durability"] --&gt; B
    D["Consistency"] --&gt; B
    B --&gt; E["No data loss&lt;br/&gt;No double-spend"]
    E --&gt; F["Financial Guarantee"]

    style F fill:#c8e6c9
</code></pre>
<h3 id="heading-performance-under-load">Performance Under Load</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Scenario</td><td>VSR</td><td>Raft</td><td>Impact</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Stable Primary</strong></td><td>No consensus overhead</td><td>Heartbeats only</td><td>VSR wins (fewer messages)</td></tr>
<tr>
<td><strong>Primary Failure</strong></td><td>Deterministic failover, &lt; 10ms</td><td>Random timeout, 100-300ms</td><td>VSR 10-30x faster</td></tr>
<tr>
<td><strong>High Throughput</strong></td><td>Batching reduces per-txn cost</td><td>Per-txn replication</td><td>VSR 100-1000x better</td></tr>
<tr>
<td><strong>Network Partitions</strong></td><td>Quorum in larger partition continues</td><td>Quorum in larger partition continues</td><td>Equivalent</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-why-this-matters-for-financial-systems">Why This Matters for Financial Systems</h2>
<ol>
<li><p><strong>Predictability:</strong> Deterministic failover means SLAs are achievable. No random election storms.</p>
</li>
<li><p><strong>Throughput:</strong> Financial transactions at scale demand near-database speeds. VSR delivers.</p>
</li>
<li><p><strong>Correctness by Design:</strong> Debit/credit invariants + VSR consensus = no double-spend bugs.</p>
</li>
<li><p><strong>Operational Simplicity:</strong> One binary, no external coordinators. Reduces failure modes in production.</p>
</li>
<li><p><strong>Audit Trail:</strong> Every transaction stamped with a global order. Compliance-friendly by default.</p>
</li>
</ol>
<hr />
<h2 id="heading-summary">Summary</h2>
<p><strong>Raft became famous because it's understandable.</strong> But understandability shouldn't be the only metric—<strong>availability and performance matter too.</strong> VSR predates Paxos (1988) and solved these problems decades ago. <strong>TigerBeetle resurrects it for a new era</strong> where financial systems demand both <strong>correctness at scale</strong> and <strong>throughput that doesn't compromise safety.</strong></p>
<p>If you're building systems where a millisecond of failover latency or a percentage point of throughput matters, VSR isn't just academic—<strong>it's a pragmatic engineering choice.</strong></p>
<hr />
<h2 id="heading-key-takeaways">Key Takeaways</h2>
<ul>
<li><p><strong>VSR = Deterministic + Passive + Efficient</strong></p>
</li>
<li><p><strong>Raft = Democratic + Active + Understandable</strong></p>
</li>
<li><p><strong>For Finance: VSR wins on latency, throughput, and predictability</strong></p>
</li>
<li><p><strong>TigerBeetle proves VSR is production-ready at scale</strong></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Precision in DeFi Math: Understanding the Balancer Exploit]]></title><description><![CDATA[TLDR
The Balancer V2 exploit of November 2025 exposed one of the most insidious vulnerabilities in DeFi—numerical precision loss in repeated calculations. Over $120-128 million in assets were stolen through a sophisticated attack that exploited round...]]></description><link>https://tech.sedhu.me/precision-in-defi-math-understanding-the-balancer-exploit</link><guid isPermaLink="true">https://tech.sedhu.me/precision-in-defi-math-understanding-the-balancer-exploit</guid><category><![CDATA[hacking]]></category><category><![CDATA[defi]]></category><dc:creator><![CDATA[Sedhu]]></dc:creator><pubDate>Thu, 06 Nov 2025 05:40:46 GMT</pubDate><content:encoded><![CDATA[<h3 id="heading-tldr">TLDR</h3>
<p>The <strong>Balancer V2 exploit of November 2025</strong> exposed one of the most insidious vulnerabilities in DeFi—<strong>numerical precision loss in repeated calculations</strong>. Over $120-128 million in assets were stolen through a sophisticated attack that exploited rounding errors in the protocol's stable swap invariant calculations. This vulnerability persisted despite <strong>11 independent security audits</strong> from leading firms, revealing a critical blind spot in how DeFi protocols handle mathematical correctness.</p>
<p>This guide walks you through:</p>
<ul>
<li><p>What went wrong technically (the math)</p>
</li>
<li><p>How the attacker exploited it (the mechanism)</p>
</li>
<li><p>How to defend against it (the solutions)</p>
</li>
<li><p>Real code examples for junior engineers to learn from</p>
</li>
</ul>
<hr />
<h2 id="heading-part-1-the-foundation-understanding-fixed-point-arithmetic-in-evm">Part 1: The Foundation - Understanding Fixed-Point Arithmetic in EVM</h2>
<h3 id="heading-why-precision-matters-in-smart-contracts">Why Precision Matters in Smart Contracts</h3>
<p>Solidity and the EVM have a fundamental limitation: <strong>they only support integer arithmetic</strong>. This creates a challenge because DeFi needs to handle fractional token amounts, prices, and yields.</p>
<p>For example:</p>
<ul>
<li><p>USDC has 6 decimal places: 1 USDC = 1,000,000 units</p>
</li>
<li><p>DAI has 18 decimal places: 1 DAI = 1,000,000,000,000,000,000 units</p>
</li>
<li><p>When you divide different decimals, you lose precision: <code>1,000,000 / 1,000,000,000,000,000,000 = 0</code> (in integer math)</p>
</li>
</ul>
<h3 id="heading-the-rounding-direction-problem">The Rounding Direction Problem</h3>
<p>Solidity's division operator <code>/</code> always <strong>rounds down</strong> (truncates). This is intentional—it's the safer direction for protecting the protocol.</p>
<pre><code class="lang-solidity"><span class="hljs-keyword">uint256</span> result <span class="hljs-operator">=</span> <span class="hljs-number">10</span> <span class="hljs-operator">/</span> <span class="hljs-number">3</span>;  <span class="hljs-comment">// Result: 3 (not 3.333...)</span>
<span class="hljs-keyword">uint256</span> remainder <span class="hljs-operator">=</span> <span class="hljs-number">10</span> <span class="hljs-operator">-</span> (result <span class="hljs-operator">*</span> <span class="hljs-number">3</span>);  <span class="hljs-comment">// Remainder: 1 wei is lost</span>
</code></pre>
<p>In DeFi, two rounding directions matter:</p>
<ol>
<li><p><strong>Round Down (divDown)</strong>: When calculating how many tokens to give OUT → Protects the pool</p>
</li>
<li><p><strong>Round Up (divUp)</strong>: When calculating how many tokens to take IN → Ensures the pool gets enough</p>
</li>
</ol>
<p>The problem: <strong>If you use the same rounding direction consistently, you create a bias that compounds over many operations.</strong></p>
<h3 id="heading-balancers-upscalingdownscaling-pattern">Balancer's Upscaling/Downscaling Pattern</h3>
<p>Balancer normalizes different decimal tokens by:</p>
<ol>
<li><p><strong>Upscaling</strong>: Multiply by a scaling factor to a common precision (typically 18 decimals) using <code>mulDown</code></p>
</li>
<li><p><strong>Calculations</strong>: Do all math at this unified precision</p>
</li>
<li><p><strong>Downscaling</strong>: Divide by the scaling factor using <code>divDown</code> or <code>divUp</code> depending on context</p>
</li>
</ol>
<p>The vulnerability: The <code>_upscaleArray()</code> function <strong>always uses</strong> <code>mulDown</code> for all tokens, regardless of whether they're inputs or outputs.</p>
<hr />
<h2 id="heading-part-2-the-balancer-v2-architecture">Part 2: The Balancer V2 Architecture</h2>
<h3 id="heading-how-balancer-stable-pools-work">How Balancer Stable Pools Work</h3>
<p>Balancer uses the <strong>Curve StableSwap invariant</strong>, a sophisticated mathematical model for stablecoin pools:</p>
<p><strong>The Invariant D Equation:</strong></p>
<pre><code class="lang-mermaid">D is calculated through an iterative Newton-Raphson method where:
D represents the total value of all tokens in the pool
Each token contributes: (balance[i] / price[i]) scaled by its weight
</code></pre>
<p>In simplified form:</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// Pseudo-code - actual implementation is more complex</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateInvariant</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> balances</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> D</span>) </span>{
    <span class="hljs-comment">// The invariant is calculated through multiple divDown operations</span>
    <span class="hljs-comment">// on scaled token balances</span>

    <span class="hljs-keyword">uint256</span> S <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> balances.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
        S <span class="hljs-operator">+</span><span class="hljs-operator">=</span> balances[i];
    }

    <span class="hljs-comment">// Multiple iterations with divDown create precision loss</span>
    D <span class="hljs-operator">=</span> S; <span class="hljs-comment">// Initial guess</span>
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> <span class="hljs-number">256</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
        <span class="hljs-keyword">uint256</span> D_prev <span class="hljs-operator">=</span> D;

        <span class="hljs-keyword">uint256</span> numerator <span class="hljs-operator">=</span> D;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> j <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; j <span class="hljs-operator">&lt;</span> balances.<span class="hljs-built_in">length</span>; j<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            <span class="hljs-comment">// Each divDown operation truncates</span>
            numerator <span class="hljs-operator">=</span> Math.divDown(numerator <span class="hljs-operator">*</span> D, balances[j] <span class="hljs-operator">*</span> balances.<span class="hljs-built_in">length</span>);
        }
        D <span class="hljs-operator">=</span> (D_prev <span class="hljs-operator">+</span> numerator) <span class="hljs-operator">/</span> <span class="hljs-number">2</span>;

        <span class="hljs-keyword">if</span> (D <span class="hljs-operator">=</span><span class="hljs-operator">=</span> D_prev) <span class="hljs-keyword">break</span>; <span class="hljs-comment">// Converged</span>
    }
}
</code></pre>
<p><strong>Critical insight</strong>: The loop contains repeated <code>divDown</code> operations. Each one truncates, and the truncations compound.</p>
<h3 id="heading-the-batch-swap-function">The Batch Swap Function</h3>
<p>Balancer's <code>batchSwap</code> function processes multiple swaps in a single transaction:</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// Simplified batchSwap structure</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">batchSwap</span>(<span class="hljs-params">
    SwapKind kind,
    BatchSwapStep[] <span class="hljs-keyword">memory</span> swaps,
    IAsset[] <span class="hljs-keyword">memory</span> assets,
    FundManagement <span class="hljs-keyword">memory</span> funds,
    <span class="hljs-keyword">int256</span>[] <span class="hljs-keyword">memory</span> limits,
    <span class="hljs-keyword">uint256</span> deadline
</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">int256</span>[] <span class="hljs-keyword">memory</span> deltas</span>) </span>{
    <span class="hljs-comment">// For each swap step:</span>
    <span class="hljs-comment">// 1. Retrieve pool from poolId</span>
    <span class="hljs-comment">// 2. Call pool.onSwap() to calculate amounts</span>
    <span class="hljs-comment">// 3. Update internal vault balances</span>
    <span class="hljs-comment">// 4. Track cumulative deltas</span>

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> swaps.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
        BatchSwapStep <span class="hljs-keyword">memory</span> swap <span class="hljs-operator">=</span> swaps[i];

        <span class="hljs-comment">// This calls the pool's swap logic which recalculates D each time</span>
        (<span class="hljs-keyword">uint256</span> amountIn, <span class="hljs-keyword">uint256</span> amountOut) <span class="hljs-operator">=</span> 
            _processSwap(swap.poolId, swap.tokenIn, swap.tokenOut, swap.amount);

        <span class="hljs-comment">// **The vulnerability**: If D is underestimated here,</span>
        <span class="hljs-comment">// the BPT price (D / totalSupply) becomes artificially low</span>
    }
}
</code></pre>
<p><strong>The attacker's insight</strong>: All these swaps happen in a single transaction. If they're carefully crafted, they can all happen while balances are positioned at rounding boundaries, <strong>causing cumulative precision loss</strong>.</p>
<hr />
<h2 id="heading-part-3-the-attack-mechanism-how-128m-was-stolen">Part 3: The Attack Mechanism - How $128M Was Stolen</h2>
<h3 id="heading-ia"> </h3>
<p><img src="https://ppl-ai-code-interpreter-files.s3.amazonaws.com/web/direct-files/3fad6ce0c2697090392bc12b1769e2bb/5d80ade0-1f84-4136-b9d5-70447c0584d4/15104bcf.png" alt /></p>
<h3 id="heading-ia-1"> </h3>
<p>Stage 1: Boundary Positioning</p>
<p>The attacker's first step was to position token amounts <strong>right at the rounding edge</strong> (8-9 wei range) where rounding effects are most dramatic.</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// This is what the attacker was targeting:</span>
<span class="hljs-keyword">uint256</span> balance <span class="hljs-operator">=</span> <span class="hljs-number">9</span>; <span class="hljs-comment">// 9 wei</span>
<span class="hljs-keyword">uint256</span> scalingFactor <span class="hljs-operator">=</span> <span class="hljs-number">10</span><span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-number">12</span>; <span class="hljs-comment">// Example scaling factor</span>

<span class="hljs-comment">// When upscaling:</span>
<span class="hljs-keyword">uint256</span> scaledBalance <span class="hljs-operator">=</span> Math.mulDown(balance, scalingFactor);
<span class="hljs-comment">// Math.mulDown: (9 * 10^12) / 10^18 = 9000000000000 / 10^18</span>
<span class="hljs-comment">// In integer math: = 0 (TRUNCATED!)</span>
<span class="hljs-comment">// Real value should be: 9 * 10^-6</span>

<span class="hljs-comment">// This rounding error gets embedded in the D calculation</span>
</code></pre>
<h3 id="heading-stage-2-the-precision-loss-cascade">Stage 2: The Precision Loss Cascade</h3>
<p>During the <code>batchSwap</code>, each step recalculates the invariant D:</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// Simplified version of what happens:</span>

<span class="hljs-comment">// Iteration 1: Balance = [1000 * 10^18, 9 * 10^18]</span>
<span class="hljs-keyword">uint256</span> D1 <span class="hljs-operator">=</span> calculateInvariant([scaled1, scaled2]);
<span class="hljs-comment">// D1 = 1009.000... (in real math)</span>
<span class="hljs-comment">// D1 = 1008.999... (due to divDown truncations)</span>
<span class="hljs-comment">// Lost: 0.001 wei (seems tiny)</span>

<span class="hljs-comment">// Iteration 2: Balance adjusted by swap</span>
<span class="hljs-keyword">uint256</span> D2 <span class="hljs-operator">=</span> calculateInvariant([scaled1_new, scaled2_new]);
<span class="hljs-comment">// D2 = 1008.998... (compound error)</span>
<span class="hljs-comment">// Lost: cumulative 0.002 wei</span>

<span class="hljs-comment">// After 100+ iterations in batchSwap:</span>
<span class="hljs-comment">// Lost: 100+ wei (starting to matter)</span>

<span class="hljs-comment">// BPT Price = D / totalSupply</span>
<span class="hljs-comment">// If D is underestimated by 100+ wei, BPT price is artificially low</span>
</code></pre>
<h3 id="heading-stage-3-invariant-deflation">Stage 3: Invariant Deflation</h3>
<p>The StableMath invariant calculation uses <strong>repeated divDown operations</strong>:</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// The actual issue - from Balancer's StableMath:</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_calculateInvariant</span>(<span class="hljs-params">
    <span class="hljs-keyword">uint256</span> D,
    <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> balances
</span>) <span class="hljs-title"><span class="hljs-keyword">private</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
    <span class="hljs-keyword">uint256</span> S <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> balances.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
        S <span class="hljs-operator">=</span> S.add(balances[i]); <span class="hljs-comment">// Adding scaled balances</span>
    }

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> <span class="hljs-number">256</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
        <span class="hljs-keyword">uint256</span> D_prev <span class="hljs-operator">=</span> D;

        <span class="hljs-comment">// THE VULNERABLE LINE:</span>
        D <span class="hljs-operator">=</span> Math.divDown(
            Math.mul(D_prev, D_prev),
            S.mul(balances[<span class="hljs-number">0</span>].mul(balances[<span class="hljs-number">1</span>]...))
        );

        <span class="hljs-comment">// Each divDown here truncates the D value downward</span>
        <span class="hljs-comment">// With carefully crafted balances, this creates a systematic bias</span>
    }

    <span class="hljs-keyword">return</span> D;
}
</code></pre>
<p><strong>Why this fails</strong>: When balances are positioned at boundaries, the denominators align such that the divDown operation always loses precision in the <strong>same direction</strong> (downward), creating a ratchet effect where D can only decrease or stay the same.</p>
<h3 id="heading-stage-4-the-arbitrage">Stage 4: The Arbitrage</h3>
<p>Once D is underestimated, the BPT price becomes artificially low:</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// Real scenario:</span>
<span class="hljs-comment">// True D = 1,000,000 * 10^18</span>
<span class="hljs-comment">// Actual D (due to rounding) = 999,900 * 10^18</span>
<span class="hljs-comment">// totalSupply = 1,000 * 10^18 (for simplicity)</span>

<span class="hljs-keyword">uint256</span> trueBPTPrice <span class="hljs-operator">=</span> <span class="hljs-number">1000000</span> <span class="hljs-operator">*</span> <span class="hljs-number">10</span><span class="hljs-operator">^</span><span class="hljs-number">18</span> <span class="hljs-operator">/</span> <span class="hljs-number">1000</span> <span class="hljs-operator">*</span> <span class="hljs-number">10</span><span class="hljs-operator">^</span><span class="hljs-number">18</span>;
<span class="hljs-comment">// = 1000 * 10^18 (1000:1 ratio)</span>

<span class="hljs-keyword">uint256</span> exploitedBPTPrice <span class="hljs-operator">=</span> <span class="hljs-number">999900</span> <span class="hljs-operator">*</span> <span class="hljs-number">10</span><span class="hljs-operator">^</span><span class="hljs-number">18</span> <span class="hljs-operator">/</span> <span class="hljs-number">1000</span> <span class="hljs-operator">*</span> <span class="hljs-number">10</span><span class="hljs-operator">^</span><span class="hljs-number">18</span>;
<span class="hljs-comment">// = 999.9 * 10^18 (999.9:1 ratio)</span>

<span class="hljs-comment">// Attacker swaps tokens FOR BPT at the deflated price</span>
<span class="hljs-comment">// They buy BPT at 999.9:1 instead of 1000:1</span>
<span class="hljs-comment">// Profit per BPT = 0.1 underlying tokens</span>
<span class="hljs-comment">// With millions in liquidity, this compounds to $millions</span>
</code></pre>
<h3 id="heading-stage-5-value-extraction">Stage 5: Value Extraction</h3>
<p>After the <code>batchSwap</code>, the attacker calls <code>manageUserBalance</code> to withdraw the accumulated internal balances:</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// The attacker's final move:</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">manageUserBalance</span>(<span class="hljs-params">UserBalanceOp[] <span class="hljs-keyword">memory</span> ops</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> ops.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
        UserBalanceOp <span class="hljs-keyword">memory</span> op <span class="hljs-operator">=</span> ops[i];

        <span class="hljs-keyword">if</span> (op.kind <span class="hljs-operator">=</span><span class="hljs-operator">=</span> UserBalanceOpKind.DEPOSIT_INTERNAL) {
            <span class="hljs-comment">// Deposit the manipulated BPT and underlying tokens</span>
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (op.kind <span class="hljs-operator">=</span><span class="hljs-operator">=</span> UserBalanceOpKind.WITHDRAW_INTERNAL) {
            <span class="hljs-comment">// **VULNERABILITY**: The access control checked:</span>
            <span class="hljs-comment">// if (msg.sender == op.sender)</span>
            <span class="hljs-comment">// But the attacker could set op.sender == msg.sender</span>
            <span class="hljs-comment">// So the check passed even though it shouldn't!</span>
        }
    }
}
</code></pre>
<hr />
<h2 id="heading-part-4-why-traditional-audits-missed-this">Part 4: Why Traditional Audits Missed This</h2>
<h3 id="heading-the-audit-blind-spot">The Audit Blind Spot</h3>
<p>Balancer underwent <strong>11 independent audits</strong> from firms including:</p>
<ul>
<li><p>OpenZeppelin</p>
</li>
<li><p>Trail of Bits</p>
</li>
<li><p>Certora</p>
</li>
<li><p>ABKD</p>
</li>
</ul>
<p>Yet the vulnerability persisted. Why?</p>
<ol>
<li><p><strong>Mathematical Edge Cases</strong>: Auditors typically look for logic bugs, not numerical edge cases that only manifest under specific boundary conditions.</p>
</li>
<li><p><strong>Static Analysis Limitations</strong>: Traditional audits use static code analysis which doesn't explore the <strong>entire state space</strong> of how values interact.</p>
</li>
<li><p><strong>Composability Complexity</strong>: Balancer V2's vault aggregates multiple pools. The vulnerability emerges from interactions between pools and the vault's accounting, not from a single function.</p>
</li>
<li><p><strong>No Invariant Testing</strong>: The audit didn't include automated testing that verifies that D monotonically increases/decreases correctly under all swap scenarios.</p>
</li>
</ol>
<hr />
<h2 id="heading-part-5-defense-mechanisms">Part 5: Defense Mechanisms</h2>
<h3 id="heading-solution-1-fixed-point-arithmetic-libraries-prbmath">Solution 1: Fixed-Point Arithmetic Libraries (PRBMath)</h3>
<p>Instead of writing custom divUp/divDown, use a vetted library:</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// DON'T DO THIS:</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">unsafeDiv</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> a, <span class="hljs-keyword">uint256</span> b</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
    <span class="hljs-keyword">return</span> a <span class="hljs-operator">/</span> b;  <span class="hljs-comment">// Rounding direction unknown</span>
}

<span class="hljs-comment">// DO THIS:</span>
<span class="hljs-keyword">import</span> {<span class="hljs-title">Math</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"prb-math/Math.sol"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">properDiv</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> a, <span class="hljs-keyword">uint256</span> b</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
    <span class="hljs-comment">// Returns the result rounded down</span>
    <span class="hljs-keyword">return</span> Math.divDown(a, b);

    <span class="hljs-comment">// Or for rounding up:</span>
    <span class="hljs-keyword">return</span> Math.divUp(a, b);
}
</code></pre>
<p><strong>PRBMath advantage</strong>: Explicitly tracks precision and handles edge cases like division by zero in a mathematically sound way.</p>
<h3 id="heading-solution-2-formal-verification-with-certora">Solution 2: Formal Verification with Certora</h3>
<p>Define invariants that must always hold and prove them mathematically:</p>
<pre><code class="lang-mermaid">// Certora Specification (pseudocode)
rule invariantMonotonicity {
    // For any swap, the invariant D should either:
    // - Increase (if adding liquidity)
    // - Decrease (if removing liquidity)
    // - Stay constant (if swapping equal values)
    // It should NEVER have rounding-induced oscillations

    uint256 D_before = calculateD();

    // Execute any swap
    executeSwap();

    uint256 D_after = calculateD();

    // Verify the change is economically sound
    assert(D_before &gt;= D_after || isAddingLiquidity());
}
</code></pre>
<p>When you run Certora on code with the vulnerability, it will find a test case that breaks this invariant.</p>
<h3 id="heading-solution-3-batch-operation-invariant-testing">Solution 3: Batch Operation Invariant Testing</h3>
<p>Use property-based testing to verify that batch operations don't accumulate errors:</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// Foundry Fuzz Test Example</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testBatchSwapInvariantHolds</span>(<span class="hljs-params">
    <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> initialBalances,
    BatchSwapStep[] <span class="hljs-keyword">memory</span> swaps
</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
    <span class="hljs-comment">// Setup: Create pool with initial balances</span>
    <span class="hljs-keyword">uint256</span> D_initial <span class="hljs-operator">=</span> calculateInvariant(initialBalances);

    <span class="hljs-comment">// Execute: Run all swaps</span>
    executeBatchSwap(swaps);

    <span class="hljs-comment">// Verify: The invariant should be consistent</span>
    <span class="hljs-keyword">uint256</span> D_final <span class="hljs-operator">=</span> calculateInvariant(getFinalBalances());

    <span class="hljs-comment">// CRITICAL: D should NEVER have rounding-induced dips</span>
    <span class="hljs-comment">// It should only change due to economic reasons (fees, slippage, etc.)</span>

    <span class="hljs-comment">// This test catches cases where rounding creates exploitable gaps</span>
    assertLessThanOrEqual(
        D_final,
        D_initial.add(expectedEconomicChange)
    );
}
</code></pre>
<p>When you fuzz this test, after ~100 runs, it will find the boundary condition that exploits the rounding error.</p>
<h3 id="heading-solution-4-bidirectional-rounding">Solution 4: Bidirectional Rounding</h3>
<p>Ensure that upscaling and downscaling use <strong>opposite</strong> rounding directions:</p>
<pre><code class="lang-solidity"><span class="hljs-comment">// BEFORE (vulnerable):</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_upscaleArray</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> balances</span>)
    <span class="hljs-title"><span class="hljs-keyword">internal</span></span>
    <span class="hljs-title"><span class="hljs-keyword">pure</span></span>
    <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> scaled</span>)
</span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> balances.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
        <span class="hljs-comment">// Always rounds DOWN</span>
        scaled[i] <span class="hljs-operator">=</span> Math.mulDown(balances[i], scalingFactor[i]);
    }
}

<span class="hljs-comment">// AFTER (fixed):</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_upscaleArray</span>(<span class="hljs-params">
    <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> balances,
    <span class="hljs-keyword">bool</span>[] <span class="hljs-keyword">memory</span> isTokenIn
</span>)
    <span class="hljs-title"><span class="hljs-keyword">internal</span></span>
    <span class="hljs-title"><span class="hljs-keyword">pure</span></span>
    <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> scaled</span>)
</span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> balances.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
        <span class="hljs-keyword">if</span> (isTokenIn[i]) {
            <span class="hljs-comment">// Token going INTO pool: round UP (ensures pool gets enough)</span>
            scaled[i] <span class="hljs-operator">=</span> Math.mulUp(balances[i], scalingFactor[i]);
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// Token going OUT of pool: round DOWN (protects pool)</span>
            scaled[i] <span class="hljs-operator">=</span> Math.mulDown(balances[i], scalingFactor[i]);
        }
    }
}
</code></pre>
<hr />
<h2 id="heading-part-6-code-examples-for-junior-engineers">Part 6: Code Examples for Junior Engineers</h2>
<h3 id="heading-example-1-simple-rounding-implementation">Example 1: Simple Rounding Implementation</h3>
<pre><code class="lang-solidity"><span class="hljs-comment">// Solidity File: Math.sol</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> 0.8.19;</span>

<span class="hljs-class"><span class="hljs-keyword">library</span> <span class="hljs-title">Math</span> </span>{
    <span class="hljs-comment">// Divide and round down (Solidity default)</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">divDown</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> a, <span class="hljs-keyword">uint256</span> b</span>) 
        <span class="hljs-title"><span class="hljs-keyword">internal</span></span> 
        <span class="hljs-title"><span class="hljs-keyword">pure</span></span> 
        <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) 
    </span>{
        <span class="hljs-keyword">return</span> a <span class="hljs-operator">/</span> b;
    }

    <span class="hljs-comment">// Divide and round up</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">divUp</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> a, <span class="hljs-keyword">uint256</span> b</span>) 
        <span class="hljs-title"><span class="hljs-keyword">internal</span></span> 
        <span class="hljs-title"><span class="hljs-keyword">pure</span></span> 
        <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) 
    </span>{
        <span class="hljs-keyword">return</span> (a <span class="hljs-operator">+</span> b <span class="hljs-operator">-</span> <span class="hljs-number">1</span>) <span class="hljs-operator">/</span> b; <span class="hljs-comment">// Ceiling division</span>
    }

    <span class="hljs-comment">// Multiply and round down</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mulDown</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> a, <span class="hljs-keyword">uint256</span> b</span>) 
        <span class="hljs-title"><span class="hljs-keyword">internal</span></span> 
        <span class="hljs-title"><span class="hljs-keyword">pure</span></span> 
        <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) 
    </span>{
        <span class="hljs-keyword">return</span> (a <span class="hljs-operator">*</span> b) <span class="hljs-operator">/</span> <span class="hljs-number">10</span><span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-number">18</span>; <span class="hljs-comment">// Assuming 18 decimal precision</span>
    }

    <span class="hljs-comment">// Multiply and round up</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mulUp</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> a, <span class="hljs-keyword">uint256</span> b</span>) 
        <span class="hljs-title"><span class="hljs-keyword">internal</span></span> 
        <span class="hljs-title"><span class="hljs-keyword">pure</span></span> 
        <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) 
    </span>{
        <span class="hljs-keyword">uint256</span> result <span class="hljs-operator">=</span> (a <span class="hljs-operator">*</span> b);
        <span class="hljs-keyword">if</span> (result <span class="hljs-operator">%</span> <span class="hljs-number">10</span><span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-number">18</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>) {
            <span class="hljs-comment">// Add 1 to round up if there's a remainder</span>
            <span class="hljs-keyword">return</span> result <span class="hljs-operator">/</span> <span class="hljs-number">10</span><span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-number">18</span> <span class="hljs-operator">+</span> <span class="hljs-number">1</span>;
        }
        <span class="hljs-keyword">return</span> result <span class="hljs-operator">/</span> <span class="hljs-number">10</span><span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-number">18</span>;
    }
}
</code></pre>
<h3 id="heading-example-2-the-vulnerable-invariant-calculation">Example 2: The Vulnerable Invariant Calculation</h3>
<pre><code class="lang-solidity"><span class="hljs-comment">// VULNERABLE - Reproduces the bug</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> 0.8.19;</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">Math</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./Math.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">VulnerableStablePool</span> </span>{
    <span class="hljs-keyword">using</span> <span class="hljs-title">Math</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title"><span class="hljs-keyword">uint256</span></span>;

    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">constant</span> AMP <span class="hljs-operator">=</span> <span class="hljs-number">5000</span>; <span class="hljs-comment">// Amplification parameter</span>
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">constant</span> PRECISION <span class="hljs-operator">=</span> <span class="hljs-number">10</span><span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-number">18</span>;

    <span class="hljs-comment">// This is the vulnerable function - it accumulates rounding errors</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateInvariant</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> balances</span>)
        <span class="hljs-title"><span class="hljs-keyword">internal</span></span>
        <span class="hljs-title"><span class="hljs-keyword">pure</span></span>
        <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)
    </span>{
        <span class="hljs-keyword">uint256</span> D <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
        <span class="hljs-keyword">uint256</span> S <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;

        <span class="hljs-comment">// First loop: calculate sum</span>
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> balances.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            S <span class="hljs-operator">=</span> S <span class="hljs-operator">+</span> balances[i]; <span class="hljs-comment">// VULNERABLE: using += without proper rounding</span>
        }

        <span class="hljs-comment">// Second loop: Newton-Raphson with rounding errors</span>
        D <span class="hljs-operator">=</span> S;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> <span class="hljs-number">256</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            <span class="hljs-keyword">uint256</span> D_prev <span class="hljs-operator">=</span> D;

            <span class="hljs-keyword">uint256</span> numerator <span class="hljs-operator">=</span> D.mulDown(D.mulDown(D)); <span class="hljs-comment">// Cascading mulDown</span>
            <span class="hljs-keyword">uint256</span> denominator <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;

            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> j <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; j <span class="hljs-operator">&lt;</span> balances.<span class="hljs-built_in">length</span>; j<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
                <span class="hljs-comment">// VULNERABLE: Each divDown here can truncate</span>
                denominator <span class="hljs-operator">=</span> denominator.mulDown(balances[j]);
            }

            <span class="hljs-comment">// VULNERABLE: Using divDown consistently creates a downward bias</span>
            D <span class="hljs-operator">=</span> (D_prev <span class="hljs-operator">+</span> numerator.divDown(denominator)) <span class="hljs-operator">/</span> <span class="hljs-number">2</span>;

            <span class="hljs-keyword">if</span> (D <span class="hljs-operator">=</span><span class="hljs-operator">=</span> D_prev) <span class="hljs-keyword">break</span>;
        }

        <span class="hljs-keyword">return</span> D;
    }
}
</code></pre>
<h3 id="heading-example-3-the-protected-version">Example 3: The Protected Version</h3>
<pre><code class="lang-solidity"><span class="hljs-comment">// PROTECTED - Defends against the bug</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> 0.8.19;</span>

<span class="hljs-keyword">import</span> {<span class="hljs-title">Math</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./Math.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">FixedStablePool</span> </span>{
    <span class="hljs-keyword">using</span> <span class="hljs-title">Math</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title"><span class="hljs-keyword">uint256</span></span>;

    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">constant</span> AMP <span class="hljs-operator">=</span> <span class="hljs-number">5000</span>;
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">constant</span> PRECISION <span class="hljs-operator">=</span> <span class="hljs-number">10</span><span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-number">18</span>;

    <span class="hljs-comment">// Add this state variable to track directions</span>
    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">uint256</span> <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> <span class="hljs-keyword">bool</span>) tokenIsInput;

    <span class="hljs-comment">// Protected version with bidirectional rounding</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">calculateInvariant</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> balances,
        <span class="hljs-keyword">bool</span>[] <span class="hljs-keyword">memory</span> directions <span class="hljs-comment">// true = input (round up), false = output (round down)</span>
    </span>)
        <span class="hljs-title"><span class="hljs-keyword">internal</span></span>
        <span class="hljs-title"><span class="hljs-keyword">pure</span></span>
        <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)
    </span>{
        <span class="hljs-keyword">uint256</span> D <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;
        <span class="hljs-keyword">uint256</span> S <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;

        <span class="hljs-comment">// Scale balances with CORRECT rounding directions</span>
        <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> scaledBalances <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-keyword">uint256</span>[](balances.<span class="hljs-built_in">length</span>);
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> balances.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            <span class="hljs-keyword">if</span> (directions[i]) {
                <span class="hljs-comment">// Input token: round UP to ensure pool gets enough</span>
                scaledBalances[i] <span class="hljs-operator">=</span> Math.mulUp(balances[i], PRECISION);
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-comment">// Output token: round DOWN to protect pool</span>
                scaledBalances[i] <span class="hljs-operator">=</span> Math.mulDown(balances[i], PRECISION);
            }
        }

        <span class="hljs-comment">// Calculate sum</span>
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> scaledBalances.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            S <span class="hljs-operator">=</span> S <span class="hljs-operator">+</span> scaledBalances[i];
        }

        <span class="hljs-comment">// Newton-Raphson with protection</span>
        D <span class="hljs-operator">=</span> S;
        <span class="hljs-keyword">uint256</span> lastError <span class="hljs-operator">=</span> <span class="hljs-keyword">type</span>(<span class="hljs-keyword">uint256</span>).<span class="hljs-built_in">max</span>;

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> <span class="hljs-number">256</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            <span class="hljs-keyword">uint256</span> D_prev <span class="hljs-operator">=</span> D;

            <span class="hljs-keyword">uint256</span> numerator <span class="hljs-operator">=</span> D.mulDown(D.mulDown(D));
            <span class="hljs-keyword">uint256</span> denominator <span class="hljs-operator">=</span> <span class="hljs-number">1</span>;

            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> j <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; j <span class="hljs-operator">&lt;</span> scaledBalances.<span class="hljs-built_in">length</span>; j<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
                denominator <span class="hljs-operator">=</span> denominator.mulDown(scaledBalances[j]);
            }

            <span class="hljs-comment">// Safe division with error tracking</span>
            <span class="hljs-keyword">uint256</span> D_new <span class="hljs-operator">=</span> (D_prev <span class="hljs-operator">+</span> numerator.divDown(denominator)) <span class="hljs-operator">/</span> <span class="hljs-number">2</span>;
            <span class="hljs-keyword">uint256</span> <span class="hljs-function"><span class="hljs-keyword">error</span> = <span class="hljs-title">D_prev</span> &gt; <span class="hljs-title">D_new</span> ? <span class="hljs-title">D_prev</span> - <span class="hljs-title">D_new</span> : <span class="hljs-title">D_new</span> - <span class="hljs-title">D_prev</span></span>;

            <span class="hljs-comment">// PROTECTION: Verify convergence isn't oscillating (rounding error)</span>
            <span class="hljs-built_in">require</span>(<span class="hljs-function"><span class="hljs-keyword">error</span> &lt;= <span class="hljs-title">lastError</span> + 1, "<span class="hljs-title">Rounding</span> <span class="hljs-title">oscillation</span> <span class="hljs-title">detected</span>")</span>;
            lastError <span class="hljs-operator">=</span> <span class="hljs-function"><span class="hljs-keyword">error</span></span>;

            D <span class="hljs-operator">=</span> D_new;
            <span class="hljs-keyword">if</span> (<span class="hljs-function"><span class="hljs-keyword">error</span> &lt; 2) <span class="hljs-title"><span class="hljs-keyword">break</span></span></span>; <span class="hljs-comment">// Converged</span>
        }

        <span class="hljs-keyword">return</span> D;
    }
}
</code></pre>
<h3 id="heading-example-4-fuzz-test-to-catch-the-vulnerability">Example 4: Fuzz Test to Catch the Vulnerability</h3>
<pre><code class="lang-solidity"><span class="hljs-comment">// Test file: StableMath.t.sol</span>
<span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> 0.8.19;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"forge-std/Test.sol"</span>;
<span class="hljs-keyword">import</span> {<span class="hljs-title">VulnerableStablePool</span>} <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../VulnerableStablePool.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">StablePoolTest</span> <span class="hljs-keyword">is</span> <span class="hljs-title">Test</span> </span>{
    VulnerableStablePool pool;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setUp</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        pool <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> VulnerableStablePool();
    }

    <span class="hljs-comment">// This fuzz test will catch the rounding bug</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testInvariantMonotonicityUnderSwaps</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> seed
    </span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-comment">// Setup initial balances at boundary conditions</span>
        <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> initialBalances <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-keyword">uint256</span>[](<span class="hljs-number">2</span>);

        <span class="hljs-comment">// KEY: Position balances at rounding boundaries (8-9 wei scale)</span>
        initialBalances[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> <span class="hljs-number">10</span><span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-number">18</span>; <span class="hljs-comment">// 1 token with 18 decimals</span>
        initialBalances[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> <span class="hljs-number">9</span>; <span class="hljs-comment">// 9 wei - RIGHT AT THE ROUNDING EDGE</span>

        <span class="hljs-keyword">uint256</span> D_initial <span class="hljs-operator">=</span> pool.calculateInvariant(initialBalances);

        <span class="hljs-comment">// Execute multiple swaps while keeping balance at boundary</span>
        <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> balanceAfterSwap <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-keyword">uint256</span>[](<span class="hljs-number">2</span>);
        balanceAfterSwap[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> <span class="hljs-number">10</span><span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-number">18</span> <span class="hljs-operator">-</span> <span class="hljs-number">1</span>;
        balanceAfterSwap[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span> <span class="hljs-number">9</span> <span class="hljs-operator">+</span> <span class="hljs-number">1</span>;

        <span class="hljs-keyword">uint256</span> D_after <span class="hljs-operator">=</span> pool.calculateInvariant(balanceAfterSwap);

        <span class="hljs-comment">// VULNERABLE CODE: D will be less than it should be due to rounding</span>
        <span class="hljs-comment">// This assertion will FAIL on the vulnerable version</span>
        assertGe(D_after, D_initial <span class="hljs-operator">-</span> <span class="hljs-number">10</span>); <span class="hljs-comment">// Allow small economic change</span>

        <span class="hljs-comment">// On the vulnerable version, this fails because:</span>
        <span class="hljs-comment">// D_initial = 1000000000000000008 (1 + 9)</span>
        <span class="hljs-comment">// D_after = 999999999999999999 (due to rounding errors)</span>
        <span class="hljs-comment">// So D_after &lt; D_initial - 10 FAILS the assertion</span>
    }
}
</code></pre>
<hr />
<h2 id="heading-part-7-real-world-application-guidelines">Part 7: Real-World Application Guidelines</h2>
<h3 id="heading-for-junior-engineers-starting-their-first-defi-project">For Junior Engineers Starting Their First DeFi Project</h3>
<ol>
<li><p><strong>Always use explicit rounding directions</strong>:</p>
<ul>
<li><p>Never rely on default Solidity division</p>
</li>
<li><p>Always use <code>divUp()</code> or <code>divDown()</code> explicitly</p>
</li>
<li><p>Comment why you chose each direction</p>
</li>
</ul>
</li>
<li><p><strong>Test invariants, not just functions</strong>:</p>
<pre><code class="lang-solidity"> <span class="hljs-comment">// DON'T test just one swap</span>
 <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testSwap</span>(<span class="hljs-params"></span>) </span>{ ... }

 <span class="hljs-comment">// DO test invariant properties</span>
 <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testSwapInvariantHolds</span>(<span class="hljs-params"></span>) </span>{
   <span class="hljs-comment">// Verify D doesn't oscillate unexpectedly</span>
   <span class="hljs-comment">// Verify BPT price matches underlying value</span>
   <span class="hljs-comment">// Verify no value created from nothing</span>
 }
</code></pre>
</li>
<li><p><strong>Use property-based testing</strong>:</p>
<pre><code class="lang-bash"> <span class="hljs-comment"># Run Foundry with many iterations</span>
 forge <span class="hljs-built_in">test</span> --fuzz-runs 10000
</code></pre>
</li>
<li><p><strong>Formal verification for critical math</strong>:</p>
<ul>
<li><p>Use Certora for any protocol that holds user funds</p>
</li>
<li><p>Write specs that capture economic properties</p>
</li>
<li><p>Don't rely on audits alone</p>
</li>
</ul>
</li>
<li><p><strong>Boundary testing</strong>:</p>
<pre><code class="lang-solidity"> <span class="hljs-comment">// Always test edge cases</span>
 <span class="hljs-operator">-</span> Very small amounts (<span class="hljs-number">1</span> <span class="hljs-literal">wei</span>)
 <span class="hljs-operator">-</span> Very large amounts (max <span class="hljs-keyword">uint256</span>)
 <span class="hljs-operator">-</span> Rounding boundaries (x and x<span class="hljs-number">.999</span>...)
 <span class="hljs-operator">-</span> Precision mismatches between tokens
</code></pre>
</li>
</ol>
<h3 id="heading-for-existing-protocol-teams">For Existing Protocol Teams</h3>
<ol>
<li><p><strong>Conduct a precision audit</strong>: Review all places where you divide and multiply, verify rounding directions</p>
</li>
<li><p><strong>Implement continuous monitoring</strong>: Track D monotonicity on-chain with emergency pause mechanisms</p>
</li>
<li><p><strong>Add redundant checks</strong>: Calculate values using two independent methods and compare</p>
</li>
<li><p><strong>Open-source invariant tests</strong>: Let the community verify your math</p>
</li>
</ol>
<hr />
<h2 id="heading-conclusion-why-this-matters">Conclusion: Why This Matters</h2>
<p>The Balancer exploit wasn't just about a bug—it was about <strong>mathematical correctness under composability</strong>. When multiple operations interact, small errors compound. The $128M loss demonstrates why precision isn't a "nice to have" but a <strong>critical security property</strong>.</p>
<p>Key takeaways:</p>
<ul>
<li><p><strong>Small rounding errors amplify in repeated operations</strong></p>
</li>
<li><p><strong>Consistent rounding directions create exploitable biases</strong></p>
</li>
<li><p><strong>Traditional audits miss numerical edge cases</strong></p>
</li>
<li><p><strong>Formal verification and fuzz testing are mandatory</strong></p>
</li>
</ul>
<p>For junior engineers, this is your reminder: The code that looks simple ("just divide these two numbers") often harbors the deepest vulnerabilities. Always think about precision, rounding, and invariants.</p>
<hr />
<h2 id="heading-references">References</h2>
<p>Articles  </p>
<p><a target="_blank" href="https://www.halborn.com/blog/post/explained-the-balancer-hack-november-2025">https://www.halborn.com/blog/post/explained-the-balancer-hack-november-2025</a></p>
<ul>
<li><h2 id="heading-github-resources">GitHub Resources</h2>
<p>  <strong>Main Repository</strong>: <a target="_blank" href="https://github.com/balancer/balancer-v2-monorepo"><strong>https://github.com/balancer/balancer-v2-monorepo</strong></a></p>
<p>  Key vulnerable files:</p>
<ul>
<li><p><code>pkg/v2-solidity-utils/contracts/math/FixedPoint.sol</code> - The upscaling function</p>
</li>
<li><p><code>pkg/v2-pool-stable/contracts/StableMath.sol</code> - The invariant calculation</p>
</li>
<li><p><code>pkg/v2-vault/contracts/Vault.sol</code> - The batch swap entry point</p>
</li>
</ul>
</li>
</ul>
<p>    <strong>Verified Packages</strong>:</p>
<ul>
<li><p>PRBMath: <a target="_blank" href="https://github.com/paulrberg/prb-math"><strong>https://github.com/paulrberg/prb-math</strong></a></p>
</li>
<li><p>Foundry (for fuzz testing): <a target="_blank" href="https://github.com/foundry-rs/foundry"><strong>https://github.com/foundry-rs/foundry</strong></a></p>
</li>
<li><p>Certora: <a target="_blank" href="https://www.certora.com/"><strong>https://www.certora.com/</strong></a></p>
</li>
</ul>
<h2 id="heading-on-chain-evidence">On-Chain Evidence</h2>
<p>    The actual attack transactions are visible on-chain:</p>
<ul>
<li><p><strong>Manipulation</strong>: <a target="_blank" href="https://etherscan.io/tx/0x6ed07db1a9fe5c0794d44cd36081d6a6df103fab868cdd75d581e3bd23bc9742"><strong>https://etherscan.io/tx/0x6ed07db1a9fe5c0794d44cd36081d6a6df103fab868cdd75d581e3bd23bc9742</strong></a></p>
</li>
<li><p><strong>Extraction</strong>: <a target="_blank" href="https://etherscan.io/tx/0xd155207261712c35fa3d472ed1e51bfcd816e616dd4f517fa5959836f5b48569"><strong>https://etherscan.io/tx/0xd155207261712c35fa3d472ed1e51bfcd816e616dd4f517fa5959836f5b48569</strong></a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Now You Can Trust AI Agents]]></title><description><![CDATA[🎯 The Big Picture: What Are These Standards?
EIP-8004 and ERC-8041 work together to create a complete ecosystem for AI agents on Ethereum:

EIP-8004 (Trustless Agents) = The foundation - an unlimited, open registry where ANY AI agent can get a uniqu...]]></description><link>https://tech.sedhu.me/now-you-can-trust-ai-agents</link><guid isPermaLink="true">https://tech.sedhu.me/now-you-can-trust-ai-agents</guid><category><![CDATA[ERC8041]]></category><category><![CDATA[EIP-8004]]></category><category><![CDATA[x402]]></category><category><![CDATA[EIPs, NFT, Web3]]></category><dc:creator><![CDATA[Sedhu]]></dc:creator><pubDate>Thu, 30 Oct 2025 17:43:26 GMT</pubDate><content:encoded><![CDATA[<hr />
<h2 id="heading-the-big-picture-what-are-these-standards"><strong>🎯 The Big Picture: What Are These Standards?</strong></h2>
<p><strong>EIP-8004</strong> and <strong>ERC-8041</strong> work together to create a complete ecosystem for AI agents on Ethereum:</p>
<ul>
<li><p><strong>EIP-8004 (Trustless Agents)</strong> = The foundation - an unlimited, open registry where ANY AI agent can get a unique on-chain identity</p>
</li>
<li><p><strong>ERC-8041 (Fixed-Supply Agent NFT Collections)</strong> = The extension - allows creation of limited-edition, curated collections of those agents</p>
</li>
</ul>
<p>Think of it like this: EIP-8004 is like getting a social security number (anyone can get one), while ERC-8041 is like being part of an exclusive club with limited membership.</p>
<hr />
<h2 id="heading-eip-8004-trustless-agents-the-foundation"><strong>📋 EIP-8004: Trustless Agents - The Foundation</strong></h2>
<h3 id="heading-core-metadata"><strong>Core Metadata</strong></h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Field</strong></td><td><strong>Value</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>EIP</strong></td><td>8004</td></tr>
<tr>
<td><strong>Title</strong></td><td>Trustless Agents</td></tr>
<tr>
<td><strong>Status</strong></td><td>DRAFT</td></tr>
<tr>
<td><strong>Type</strong></td><td>Standards Track</td></tr>
<tr>
<td><strong>Category</strong></td><td>ERC</td></tr>
<tr>
<td><strong>Authors</strong></td><td>Marco De Rossi, Davide Crapis, Jordan Ellis, Erik Reppel</td></tr>
<tr>
<td><strong>Created</strong></td><td>2025-08-13</td></tr>
</tbody>
</table>
</div><h3 id="heading-what-problem-does-it-solve"><strong>What Problem Does It Solve?</strong></h3>
<p>EIP-8004 addresses a critical gap in the AI agent ecosystem: <strong>How do you discover, trust, and interact with AI agents across different organizations without pre-existing relationships?</strong></p>
<p>Current protocols like MCP (Model Context Protocol) and A2A (Agent-to-Agent) handle communication, but they don't solve:</p>
<ul>
<li><p><strong>Discovery</strong>: How do you find agents?</p>
</li>
<li><p><strong>Identity</strong>: How do you verify an agent's identity?</p>
</li>
<li><p><strong>Trust</strong>: How do you know an agent will perform as promised?</p>
</li>
</ul>
<h3 id="heading-the-three-registry-architecture"><strong>The Three-Registry Architecture</strong></h3>
<p>EIP-8004 introduces three interconnected registries:</p>
<h4 id="heading-1-identity-registry-erc-721-based"><strong>1. Identity Registry (ERC-721 Based)</strong></h4>
<p>This is the core registry where every agent gets a unique, globally-identifiable token.</p>
<p><strong>Global Agent Identifier Format:</strong></p>
<p>plaintext</p>
<pre><code class="lang-plaintext">eip155:{chainId}:{identityRegistry}:{agentId}
</code></pre>
<p><strong>Example:</strong> <code>eip155:1:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb7:42</code></p>
<p><strong>Key Functions:</strong></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// Register a new agent</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">register</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> tokenURI, MetadataEntry[] <span class="hljs-keyword">calldata</span> metadata</span>) 
    <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> agentId</span>)</span>;

<span class="hljs-comment">// Set on-chain metadata</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setMetadata</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> agentId, <span class="hljs-keyword">string</span> key, <span class="hljs-keyword">bytes</span> value</span>)</span>;

<span class="hljs-comment">// Get on-chain metadata</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getMetadata</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> agentId, <span class="hljs-keyword">string</span> key</span>) 
    <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bytes</span> value</span>)</span>;
</code></pre>
<p><strong>Agent Registration File Structure:</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"type"</span>: <span class="hljs-string">"https://eips.ethereum.org/EIPS/eip-8004#registration-v1"</span>,
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"myAgentName"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"What the agent does, pricing, etc."</span>,
  <span class="hljs-attr">"image"</span>: <span class="hljs-string">"https://example.com/agentimage.png"</span>,
  <span class="hljs-attr">"endpoints"</span>: [
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"A2A"</span>,
      <span class="hljs-attr">"endpoint"</span>: <span class="hljs-string">"https://agent.example/.well-known/agent-card.json"</span>,
      <span class="hljs-attr">"version"</span>: <span class="hljs-string">"0.3.0"</span>
    },
    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"MCP"</span>,
      <span class="hljs-attr">"endpoint"</span>: <span class="hljs-string">"https://mcp.agent.eth/"</span>,
      <span class="hljs-attr">"version"</span>: <span class="hljs-string">"2025-06-18"</span>
    }
  ],
  <span class="hljs-attr">"supportedTrust"</span>: [
    <span class="hljs-string">"reputation"</span>,
    <span class="hljs-string">"crypto-economic"</span>,
</code></pre>
<h4 id="heading-2-reputation-registry-trust-through-feedback"><strong>2. Reputation Registry (Trust Through Feedback)</strong></h4>
<p>This registry allows clients to leave feedback about their experiences with agents, creating a decentralized reputation system.</p>
<p><strong>The Feedback Flow:</strong></p>
<ol>
<li><p><strong>Agent authorizes feedback</strong> by signing a <code>feedbackAuth</code> when accepting a task</p>
</li>
<li><p><strong>Client gives feedback</strong> after task completion</p>
</li>
<li><p><strong>Feedback is permanently recorded</strong> on-chain with optional off-chain details</p>
</li>
</ol>
<p><strong>Core Function:</strong></p>
<pre><code class="lang-solidity"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">giveFeedback</span>(<span class="hljs-params">
    <span class="hljs-keyword">uint256</span> agentId,
    <span class="hljs-keyword">uint8</span> score,           <span class="hljs-comment">// 0-100</span>
    <span class="hljs-keyword">bytes32</span> tag1,          <span class="hljs-comment">// e.g., "skill-type"</span>
    <span class="hljs-keyword">bytes32</span> tag2,          <span class="hljs-comment">// e.g., "task-category"</span>
    <span class="hljs-keyword">string</span> <span class="hljs-keyword">calldata</span> fileuri,
    <span class="hljs-keyword">bytes32</span> <span class="hljs-keyword">calldata</span> filehash,
    <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> feedbackAuth
</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span></span>;
</code></pre>
<p><strong>Events:</strong></p>
<pre><code class="lang-solidity"><span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">NewFeedback</span>(<span class="hljs-params">
    <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">indexed</span> agentId,
    <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> clientAddress,
    <span class="hljs-keyword">uint8</span> score,
    <span class="hljs-keyword">bytes32</span> <span class="hljs-keyword">indexed</span> tag1,
    <span class="hljs-keyword">bytes32</span> tag2,
    <span class="hljs-keyword">string</span> fileuri,
    <span class="hljs-keyword">bytes32</span> filehash
</span>)</span>;
</code></pre>
<p><strong>Off-Chain Feedback File Example:</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"agentRegistry"</span>: <span class="hljs-string">"eip155:1:{identityRegistry}"</span>,
  <span class="hljs-attr">"agentId"</span>: <span class="hljs-number">22</span>,
  <span class="hljs-attr">"clientAddress"</span>: <span class="hljs-string">"eip155:1:{clientAddress}"</span>,
  <span class="hljs-attr">"score"</span>: <span class="hljs-number">100</span>,
  <span class="hljs-attr">"tag1"</span>: <span class="hljs-string">"data-analysis"</span>,
  <span class="hljs-attr">"tag2"</span>: <span class="hljs-string">"financial"</span>,
  <span class="hljs-attr">"skill"</span>: <span class="hljs-string">"as-defined-by-A2A"</span>,
  <span class="hljs-attr">"proof_of_payment"</span>: {
    <span class="hljs-attr">"fromAddress"</span>: <span class="hljs-string">"0x00..."</span>,
    <span class="hljs-attr">"toAddress"</span>: <span class="hljs-string">"0x00..."</span>,
    <span class="hljs-attr">"chainId"</span>: <span class="hljs-string">"1"</span>,
    <span class="hljs-attr">"txHash"</span>: <span class="hljs-string">"0x00..."</span>
  }
}
</code></pre>
<p><strong>Read Functions:</strong></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// Get summary statistics</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getSummary</span>(<span class="hljs-params">
    <span class="hljs-keyword">uint256</span> agentId,
    <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span> clientAddresses,
    <span class="hljs-keyword">bytes32</span> tag1,
    <span class="hljs-keyword">bytes32</span> tag2
</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint64</span> count, <span class="hljs-keyword">uint8</span> averageScore</span>)</span>;

<span class="hljs-comment">// Read all feedback with filters</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">readAllFeedback</span>(<span class="hljs-params">
    <span class="hljs-keyword">uint256</span> agentId,
    <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span> clientAddresses,
    <span class="hljs-keyword">bytes32</span> tag1,
    <span class="hljs-keyword">bytes32</span> tag2,
    <span class="hljs-keyword">bool</span> includeRevoked
</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">
    <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">memory</span> clientAddresses,
    <span class="hljs-keyword">uint8</span>[] <span class="hljs-keyword">memory</span> scores,
    <span class="hljs-keyword">bytes32</span>[] <span class="hljs-keyword">memory</span> tag1s,
    <span class="hljs-keyword">bytes32</span>[] <span class="hljs-keyword">memory</span> tag2s,</span></span>
</code></pre>
<h4 id="heading-3-validation-registry-cryptographic-verification"><strong>3. Validation Registry (Cryptographic Verification)</strong></h4>
<p>For high-stakes tasks, reputation isn't enough. The Validation Registry enables agents to request independent verification of their work through:</p>
<ul>
<li><p><strong>Staked re-execution</strong> (validators re-run the task and stake on the result)</p>
</li>
<li><p><strong>zkML proofs</strong> (zero-knowledge machine learning verification)</p>
</li>
<li><p><strong>TEE attestations</strong> (Trusted Execution Environment proofs)</p>
</li>
</ul>
<p><strong>Request Validation:</strong></p>
<pre><code class="lang-solidity"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validationRequest</span>(<span class="hljs-params">
    <span class="hljs-keyword">address</span> validatorAddress,
    <span class="hljs-keyword">uint256</span> agentId,
    <span class="hljs-keyword">string</span> requestUri,
    <span class="hljs-keyword">bytes32</span> requestHash
</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span></span>;
</code></pre>
<p><strong>Validator Response:</strong></p>
<pre><code class="lang-solidity"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validationResponse</span>(<span class="hljs-params">
    <span class="hljs-keyword">bytes32</span> requestHash,
    <span class="hljs-keyword">uint8</span> response,        <span class="hljs-comment">// 0-100 score</span>
    <span class="hljs-keyword">string</span> responseUri,
    <span class="hljs-keyword">bytes32</span> responseHash,
    <span class="hljs-keyword">bytes32</span> tag
</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span></span>;
</code></pre>
<h3 id="heading-trust-models-tiered-security"><strong>Trust Models: Tiered Security</strong></h3>
<p>EIP-8004 supports <strong>pluggable trust models</strong> with security proportional to value at risk:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Trust Level</strong></td><td><strong>Use Case</strong></td><td><strong>Mechanism</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>Low-stake</strong></td><td>Ordering pizza, simple queries</td><td>Reputation only</td></tr>
<tr>
<td><strong>Medium-stake</strong></td><td>Data analysis, content creation</td><td>Reputation + validation sampling</td></tr>
<tr>
<td><strong>High-stake</strong></td><td>Medical diagnosis, financial advice</td><td>zkML proofs or TEE attestation required</td></tr>
</tbody>
</table>
</div><h3 id="heading-security-considerations"><strong>Security Considerations</strong></h3>
<ul>
<li><p><strong>Spam/Sybil attacks</strong>: Only partially mitigated; relies on ecosystem reputation systems</p>
</li>
<li><p><strong>Audit trail</strong>: On-chain pointers and hashes ensure integrity</p>
</li>
<li><p><strong>No cryptographic guarantee</strong>: The protocol can't guarantee advertised capabilities are real - trust models address this</p>
</li>
</ul>
<hr />
<h2 id="heading-erc-8041-fixed-supply-agent-nft-collections"><strong>🎨 ERC-8041: Fixed-Supply Agent NFT Collections</strong></h2>
<h3 id="heading-core-metadata-1"><strong>Core Metadata</strong></h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Field</strong></td><td><strong>Value</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>EIP</strong></td><td>8041</td></tr>
<tr>
<td><strong>Title</strong></td><td>Fixed-Supply Agent NFT Collections</td></tr>
<tr>
<td><strong>Status</strong></td><td>Draft</td></tr>
<tr>
<td><strong>Type</strong></td><td>Standards Track</td></tr>
<tr>
<td><strong>Category</strong></td><td>ERC</td></tr>
<tr>
<td><strong>Author</strong></td><td>Prem Makeig (@nxt3d)</td></tr>
<tr>
<td><strong>Created</strong></td><td>2025-10-11</td></tr>
<tr>
<td><strong>Requires</strong></td><td>ERC-8004, ERC-7572</td></tr>
</tbody>
</table>
</div><h3 id="heading-what-problem-does-it-solve-1"><strong>What Problem Does It Solve?</strong></h3>
<p>While EIP-8004 provides unlimited agent registration, many use cases require:</p>
<ol>
<li><p><strong>Fixed-supply collections</strong> - Limited editions (e.g., "Genesis 100", "Season 1")</p>
</li>
<li><p><strong>Collection metadata</strong> - Associate agents with specific collections</p>
</li>
<li><p><strong>Mint number tracking</strong> - Permanent record of position (#5 of 1000)</p>
</li>
<li><p><strong>Time-gated releases</strong> - Collections that activate at specific blocks</p>
</li>
<li><p><strong>Curated drops</strong> - Controlled minting vs. open registration</p>
</li>
</ol>
<h3 id="heading-collection-structure"><strong>Collection Structure</strong></h3>
<p>Every ERC-8041 collection MUST maintain:</p>
<pre><code class="lang-solidity"><span class="hljs-keyword">uint256</span> maxSupply;      <span class="hljs-comment">// Maximum agents in collection</span>
<span class="hljs-keyword">uint256</span> currentSupply;  <span class="hljs-comment">// Current number minted</span>
<span class="hljs-keyword">uint256</span> startBlock;     <span class="hljs-comment">// When minting becomes available</span>
<span class="hljs-keyword">bool</span> open;              <span class="hljs-comment">// Currently accepting mints?</span>
</code></pre>
<h3 id="heading-core-interface"><strong>Core Interface</strong></h3>
<pre><code class="lang-solidity"><span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">IERC8041Collection</span> </span>{
    <span class="hljs-comment">// Events</span>
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">CollectionCreated</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> maxSupply, <span class="hljs-keyword">uint256</span> startBlock</span>)</span>;
    <span class="hljs-function"><span class="hljs-keyword">event</span> <span class="hljs-title">AgentMinted</span>(<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> <span class="hljs-keyword">indexed</span> agentId,
        <span class="hljs-keyword">uint256</span> mintNumber,
        <span class="hljs-keyword">address</span> <span class="hljs-keyword">indexed</span> owner
    </span>)</span>;

    <span class="hljs-comment">// Get agent's position in collection (0 if not in collection)</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAgentMintNumber</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> agentId</span>) 
        <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span> mintNumber</span>)</span>;

    <span class="hljs-comment">// Get collection details</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getCollectionDetails</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params">
        <span class="hljs-keyword">uint256</span> maxSupply,
        <span class="hljs-keyword">uint256</span> currentSupply,
        <span class="hljs-keyword">uint256</span> startBlock,
        <span class="hljs-keyword">bool</span> open
    </span>)</span>;
</code></pre>
<h3 id="heading-the-critical-link-collection-metadata"><strong>The Critical Link: Collection Metadata</strong></h3>
<p>When an agent is minted through an ERC-8041 collection, the collection contract writes metadata to the agent's EIP-8004 token using the key <code>"agent-collection"</code>:</p>
<p><strong>Metadata Format:</strong></p>
<pre><code class="lang-solidity"><span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(
    <span class="hljs-keyword">address</span> collectionAddress,  <span class="hljs-comment">// 20 bytes: collection contract</span>
    <span class="hljs-keyword">uint8</span> mintNumberLength,     <span class="hljs-comment">// 1 byte: length of mint number</span>
    <span class="hljs-keyword">bytes</span> mintNumberBytes       <span class="hljs-comment">// variable: the mint number</span>
)
</code></pre>
<p><strong>Example Implementation:</strong></p>
<pre><code class="lang-solidity"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mint</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> agentId</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
    <span class="hljs-built_in">require</span>(currentSupply <span class="hljs-operator">&lt;</span> maxSupply, <span class="hljs-string">"Supply exceeded"</span>);
    <span class="hljs-built_in">require</span>(agentRegistry.ownerOf(agentId) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, <span class="hljs-string">"Not owner"</span>);
    <span class="hljs-built_in">require</span>(_agentMintNumber[agentId] <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-number">0</span>, <span class="hljs-string">"Already in collection"</span>);

    currentSupply<span class="hljs-operator">+</span><span class="hljs-operator">+</span>;
    <span class="hljs-keyword">uint256</span> mintNumber <span class="hljs-operator">=</span> currentSupply;
    _agentMintNumber[agentId] <span class="hljs-operator">=</span> mintNumber;

    <span class="hljs-comment">// Encode and store collection metadata</span>
    <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> mintNumberBytes <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(mintNumber);
    <span class="hljs-keyword">uint8</span> mintNumberLength <span class="hljs-operator">=</span> <span class="hljs-keyword">uint8</span>(mintNumberBytes.<span class="hljs-built_in">length</span>);
    <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> collectionMetadata <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodePacked</span>(
        <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>),
        mintNumberLength,
        mintNumberBytes
    );

    <span class="hljs-comment">// Write to EIP-8004 registry</span>
    IOnchainMetadata(<span class="hljs-keyword">address</span>(agentRegistry)).setMetadataByContract(
</code></pre>
<h3 id="heading-critical-security-consideration"><strong>🔒 Critical Security Consideration</strong></h3>
<p><strong>The agent owner can modify or remove the</strong> <code>"agent-collection"</code> metadata at any time!</p>
<p>Therefore, clients MUST:</p>
<ol>
<li><p>✅ <strong>Query the collection contract directly</strong> using <code>getAgentMintNumber(agentId)</code> for verification</p>
</li>
<li><p>✅ Use metadata only as a <strong>convenience lookup</strong> to discover which collection to query</p>
</li>
<li><p>✅ Treat <code>mintNumber == 0</code> from the collection contract as definitive proof the agent is NOT in the collection</p>
</li>
</ol>
<p><strong>Never trust the metadata alone!</strong></p>
<hr />
<h2 id="heading-how-they-work-together-the-complete-flow"><strong>🔗 How They Work Together: The Complete Flow</strong></h2>
<h3 id="heading-scenario-gaming-company-launches-ai-character-collection"><strong>Scenario: Gaming Company Launches AI Character Collection</strong></h3>
<p><strong>Step 1: Deploy EIP-8004 Registry (One-time, per chain)</strong></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// Deployed once as a singleton on the chain</span>
IdentityRegistry registry <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> IdentityRegistry();
</code></pre>
<p><strong>Step 2: Deploy ERC-8041 Collection Contract</strong></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// Gaming company creates "Season 1 Heroes" collection</span>
ERC8041Collection season1 <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> ERC8041Collection(
    registry,        <span class="hljs-comment">// EIP-8004 registry address</span>
    <span class="hljs-number">10000</span>           <span class="hljs-comment">// maxSupply: 10,000 heroes</span>
);
</code></pre>
<p><strong>Step 3: User Mints a Character</strong></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// User calls the collection contract</span>
season1.mint(agentId);

<span class="hljs-comment">// Behind the scenes:</span>
<span class="hljs-comment">// 1. Agent is registered in EIP-8004 registry (if not already)</span>
<span class="hljs-comment">// 2. Mint number is assigned (e.g., #42)</span>
<span class="hljs-comment">// 3. Collection metadata is written to agent's EIP-8004 token</span>
<span class="hljs-comment">// 4. AgentMinted event is emitted</span>
</code></pre>
<p><strong>Step 4: Verification Flow</strong></p>
<pre><code class="lang-solidity"><span class="hljs-comment">// Client wants to verify agent #123 is in Season 1 collection</span>

<span class="hljs-comment">// Step 4a: Read metadata from EIP-8004 (convenience lookup)</span>
<span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> metadata <span class="hljs-operator">=</span> registry.getMetadata(<span class="hljs-number">123</span>, <span class="hljs-string">"agent-collection"</span>);
<span class="hljs-keyword">address</span> collectionAddr <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-keyword">bytes20</span>(metadata[<span class="hljs-number">0</span>:<span class="hljs-number">20</span>]));

<span class="hljs-comment">// Step 4b: Verify with collection contract (definitive proof)</span>
<span class="hljs-keyword">uint256</span> mintNumber <span class="hljs-operator">=</span> season1.getAgentMintNumber(<span class="hljs-number">123</span>);
<span class="hljs-comment">// Returns: 42 (agent is #42 of 10,000)</span>
<span class="hljs-comment">// If returns 0, agent is NOT in this collection</span>
</code></pre>
<hr />
<h2 id="heading-key-concepts"><strong>💡 Key Concepts</strong></h2>
<h3 id="heading-1-unlimited-vs-fixed-supply"><strong>1. Unlimited vs. Fixed Supply</strong></h3>
<ul>
<li><p><strong>EIP-8004</strong>: Open, permissionless, unlimited minting - anyone can register</p>
</li>
<li><p><strong>ERC-8041</strong>: Curated, limited supply - controlled by collection creator</p>
</li>
</ul>
<h3 id="heading-2-identity-vs-collection"><strong>2. Identity vs. Collection</strong></h3>
<ul>
<li><p><strong>EIP-8004</strong>: Provides the identity layer (the "passport")</p>
</li>
<li><p><strong>ERC-8041</strong>: Provides the collection layer (the "exclusive club membership")</p>
</li>
</ul>
<h3 id="heading-3-trust-hierarchy"><strong>3. Trust Hierarchy</strong></h3>
<pre><code class="lang-plaintext">Low Stakes → Reputation only
     ↓
Medium Stakes → Reputation + Validation sampling
     ↓
High Stakes → zkML proofs or TEE attestation
</code></pre>
<h3 id="heading-4-metadata-vs-contract-truth"><strong>4. Metadata vs. Contract Truth</strong></h3>
<ul>
<li><p>Metadata = Convenience (can be changed by owner)</p>
</li>
<li><p>Contract query = Truth (immutable record)</p>
</li>
</ul>
<hr />
<h2 id="heading-practical-use-cases"><strong>🛠️ Practical Use Cases</strong></h2>
<h3 id="heading-eip-8004-standalone"><strong>EIP-8004 Standalone</strong></h3>
<ul>
<li><p><strong>DeFi Protocol Agent</strong>: Single AI agent managing a protocol, building reputation over time</p>
</li>
<li><p><strong>Personal AI Assistant</strong>: Your own AI with portable identity across platforms</p>
</li>
<li><p><strong>Research Agent</strong>: Academic AI building credibility through validated outputs</p>
</li>
</ul>
<h3 id="heading-erc-8041-eip-8004"><strong>ERC-8041 + EIP-8004</strong></h3>
<ul>
<li><p><strong>Gaming</strong>: 10,000 unique AI-powered NPCs with provenance</p>
</li>
<li><p><strong>Art</strong>: Limited edition AI artists (e.g., "Genesis 100 AI Creators")</p>
</li>
<li><p><strong>Professional Services</strong>: Curated collection of verified medical/legal AI agents</p>
</li>
<li><p><strong>Seasonal Drops</strong>: "Q1 2025 Trading Bots" - limited to 500</p>
</li>
</ul>
<hr />
<h2 id="heading-technical-comparison"><strong>📊 Technical Comparison</strong></h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Feature</strong></td><td><strong>EIP-8004</strong></td><td><strong>ERC-8041</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>Supply</strong></td><td>Unlimited</td><td>Fixed per collection</td></tr>
<tr>
<td><strong>Minting</strong></td><td>Open to anyone</td><td>Controlled by collection</td></tr>
<tr>
<td><strong>Purpose</strong></td><td>Identity &amp; trust infrastructure</td><td>Curation &amp; scarcity</td></tr>
<tr>
<td><strong>Mint Numbers</strong></td><td>No</td><td>Yes (1-indexed, sequential)</td></tr>
<tr>
<td><strong>Trust Models</strong></td><td>Reputation, Validation, TEE</td><td>Inherits from EIP-8004</td></tr>
<tr>
<td><strong>Metadata</strong></td><td>Agent registration file</td><td>Collection association</td></tr>
<tr>
<td><strong>Base Standard</strong></td><td>ERC-721</td><td>ERC-721 + EIP-8004</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-master-level-insights"><strong>🎓 Master-Level Insights</strong></h2>
<h3 id="heading-1-gas-optimization-strategy"><strong>1. Gas Optimization Strategy</strong></h3>
<p>EIP-8004 is designed for EIP-7702 compatibility, allowing applications to sponsor gas for frictionless feedback submission. This is crucial for adoption.</p>
<h3 id="heading-2-indexing-amp-discovery"><strong>2. Indexing &amp; Discovery</strong></h3>
<p>Both standards are designed for easy indexing:</p>
<ul>
<li><p>On-chain events provide complete audit trail</p>
</li>
<li><p>IPFS usage enables decentralized storage</p>
</li>
<li><p>Subgraph-friendly architecture</p>
</li>
</ul>
<h3 id="heading-3-cross-chain-agents"><strong>3. Cross-Chain Agents</strong></h3>
<p>Agents can operate across multiple chains while maintaining a single identity:</p>
<pre><code class="lang-json"><span class="hljs-string">"registrations"</span>: [
  {<span class="hljs-attr">"agentId"</span>: <span class="hljs-number">22</span>, <span class="hljs-attr">"agentRegistry"</span>: <span class="hljs-string">"eip155:1:{registry}"</span>},
  {<span class="hljs-attr">"agentId"</span>: <span class="hljs-number">45</span>, <span class="hljs-attr">"agentRegistry"</span>: <span class="hljs-string">"eip155:137:{registry}"</span>}
]
</code></pre>
<h3 id="heading-4-composability"><strong>4. Composability</strong></h3>
<p>The three-registry architecture is intentionally modular:</p>
<ul>
<li><p>Use Identity only for basic discovery</p>
</li>
<li><p>Add Reputation for trust signals</p>
</li>
<li><p>Add Validation for high-stakes verification  </p>
</li>
</ul>
<h1 id="heading-medical-record-verification-system"><strong>Medical Record Verification System</strong></h1>
<h2 id="heading-system-overview"><strong>🎯 System Overview</strong></h2>
<p>A blockchain-based medical record verification system that combines <strong>EIP-8004</strong> (agent identity &amp; trust), <strong>ERC-8041</strong> (certified medical agent collections), and <strong>HTTP 402</strong> (payment-required protocol) to create a secure, auditable healthcare AI ecosystem.</p>
<hr />
<h2 id="heading-core-components"><strong>📋 Core Components</strong></h2>
<h3 id="heading-1-eip-8004-agent-identity-amp-trust-infrastructure"><strong>1. EIP-8004: Agent Identity &amp; Trust Infrastructure</strong></h3>
<ul>
<li><p><strong>Identity Registry</strong>: Each medical AI gets unique on-chain identity (ERC-721)</p>
</li>
<li><p><strong>Reputation Registry</strong>: Doctors submit feedback after using agents</p>
</li>
<li><p><strong>Validation Registry</strong>: TEE attestations prove secure execution</p>
</li>
</ul>
<h3 id="heading-2-erc-8041-certified-medical-agent-collections"><strong>2. ERC-8041: Certified Medical Agent Collections</strong></h3>
<ul>
<li><p>Government/hospital creates limited collection (e.g., "FDA-Cleared Diagnostic Agents")</p>
</li>
<li><p>Only 100 certified agents allowed</p>
</li>
<li><p>Each agent gets mint number (#1 of 100) for provenance</p>
</li>
<li><p>Collection metadata proves certification</p>
</li>
</ul>
<h3 id="heading-3-http-402-payment-protocol"><strong>3. HTTP 402: Payment Protocol</strong></h3>
<ul>
<li><p>Agent returns <code>402 Payment Required</code> before service</p>
</li>
<li><p>Client pays on-chain (e.g., 50 USDC)</p>
</li>
<li><p>Payment proof included in feedback</p>
</li>
<li><p>Creates economic accountability</p>
</li>
</ul>
<pre><code class="lang-mermaid">sequenceDiagram
    participant P as Patient/Hospital
    participant MA as Medical Agent
    participant IR as Identity Registry&lt;br/&gt;(EIP-8004)
    participant C as Certified Collection&lt;br/&gt;(ERC-8041)
    participant RR as Reputation Registry&lt;br/&gt;(EIP-8004)
    participant VR as Validation Registry&lt;br/&gt;(EIP-8004)
    participant TEE as TEE Oracle&lt;br/&gt;(Intel SGX)
    participant BC as Blockchain

    Note over P,BC: PHASE 1: DISCOVERY &amp; VERIFICATION
    P-&gt;&gt;IR: Query agent credentials
    IR--&gt;&gt;P: Agent profile + endpoints
    P-&gt;&gt;C: Verify certification
    C--&gt;&gt;P: Mint #42 of 100 (certified)
    P-&gt;&gt;RR: Check reputation
    RR--&gt;&gt;P: 95/100 avg score (500 cases)

    Note over P,BC: PHASE 2: PAYMENT (HTTP 402)
    P-&gt;&gt;MA: POST /diagnose (patient data)
    MA--&gt;&gt;P: 402 Payment Required&lt;br/&gt;(0.5 ETH to 0x...Agent)
    P-&gt;&gt;BC: Transfer 50 USDC
    BC--&gt;&gt;P: Tx: 0xabc123...
    P-&gt;&gt;MA: POST /diagnose + payment_proof

    Note over P,BC: PHASE 3: DIAGNOSIS &amp; TEE VALIDATION
    MA-&gt;&gt;TEE: Run diagnosis in SGX enclave
    TEE--&gt;&gt;MA: Diagnosis + attestation quote
    MA-&gt;&gt;VR: Request validation
    VR-&gt;&gt;TEE: Verify SGX attestation
    TEE--&gt;&gt;VR: Attestation valid ✓
    VR-&gt;&gt;BC: Record validation (100/100)
    MA--&gt;&gt;P: Diagnosis + TEE proof

    Note over P,BC: PHASE 4: FEEDBACK WITH PAYMENT PROOF
    P-&gt;&gt;RR: Submit feedback&lt;br/&gt;(score, payment_proof, outcome)
    RR-&gt;&gt;BC: Record feedback event
    BC--&gt;&gt;RR: Feedback stored ✓
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Rate limiting - A Probabilistic Approach]]></title><description><![CDATA[The Simple Explanation
Imagine you're managing a very popular restaurant. You need to know two things:

How many different people visited today?

How many times did each person order food?


There two clever tools: HyperLogLog (HLL) and Count-Min Ske...]]></description><link>https://tech.sedhu.me/rate-limiting-a-probabilistic-approach</link><guid isPermaLink="true">https://tech.sedhu.me/rate-limiting-a-probabilistic-approach</guid><category><![CDATA[rate-limiting]]></category><category><![CDATA[probability]]></category><dc:creator><![CDATA[Sedhu]]></dc:creator><pubDate>Wed, 14 May 2025 15:50:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/5d2QJl88QbI/upload/d601ecf2b6d22e610765897208bdfb19.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-the-simple-explanation">The Simple Explanation</h2>
<p>Imagine you're managing a very popular restaurant. You need to know two things:</p>
<ul>
<li><p>How many different people visited today?</p>
</li>
<li><p>How many times did each person order food?</p>
</li>
</ul>
<p>There two clever tools: HyperLogLog (HLL) and Count-Min Sketch.</p>
<h3 id="heading-hyperloglog-hll-the-unique-visitor-counter">HyperLogLog (HLL) - The Unique Visitor Counter</h3>
<p>Think of HLL like a magical guest book that:</p>
<ul>
<li><p>Only needs a tiny amount of space (1.5KB)</p>
</li>
<li><p>Can estimate millions of unique visitors</p>
</li>
<li><p>Might be off by 2% (which is totally fine!)</p>
</li>
</ul>
<pre><code class="lang-mermaid">graph TD
    A["Visitors"] --&gt; B["Magic Guest Book (HLL)"]
    B --&gt; C["Approximate Count"]
    style B fill:#f9f,stroke:#333
</code></pre>
<h3 id="heading-count-min-sketch-the-smart-traffic-counter">Count-Min Sketch - The Smart Traffic Counter</h3>
<p>This is like having multiple security cameras counting how often each person comes in:</p>
<ul>
<li><p>Each camera counts a bit differently</p>
</li>
<li><p>By combining all cameras' counts, we get a good estimate</p>
</li>
<li><p>Catches people who are visiting too frequently</p>
</li>
</ul>
<pre><code class="lang-mermaid">graph LR
    A["Requests"] --&gt; B["Multiple Counters"]
    B --&gt; C["Counter 1"]
    B --&gt; D["Counter 2"]
    B --&gt; E["Counter 3"]
    C &amp; D &amp; E --&gt; F["Final Count"]
</code></pre>
<h2 id="heading-the-technical-deep-dive">The Technical Deep-Dive</h2>
<p>Now let's look under the hood at how these systems actually work.</p>
<h3 id="heading-hyperloglog-implementation">HyperLogLog Implementation</h3>
<pre><code class="lang-jsx"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HyperLogLog</span> </span>{
    <span class="hljs-keyword">constructor</span>(precision = 14) {
        <span class="hljs-built_in">this</span>.precision = precision;
        <span class="hljs-built_in">this</span>.m = <span class="hljs-number">1</span> &lt;&lt; precision; <span class="hljs-comment">// Number of registers</span>
        <span class="hljs-built_in">this</span>.registers = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(<span class="hljs-built_in">this</span>.m).fill(<span class="hljs-number">0</span>);
    }

    add(element) {
        <span class="hljs-keyword">const</span> hash = <span class="hljs-built_in">this</span>.hashFunction(element);
        <span class="hljs-keyword">const</span> bucket = hash &amp; (<span class="hljs-built_in">this</span>.m - <span class="hljs-number">1</span>); <span class="hljs-comment">// Determine register</span>
        <span class="hljs-keyword">const</span> w = hash &gt;&gt;&gt; <span class="hljs-built_in">this</span>.precision;
        <span class="hljs-built_in">this</span>.registers[bucket] = <span class="hljs-built_in">Math</span>.max(
            <span class="hljs-built_in">this</span>.registers[bucket],
            <span class="hljs-built_in">this</span>.leadingZeros(w) + <span class="hljs-number">1</span>
        );
    }

    estimate() {
        <span class="hljs-comment">// Harmonic mean calculation for cardinality estimation</span>
        <span class="hljs-keyword">const</span> harmonicMean = <span class="hljs-built_in">this</span>.registers.reduce(<span class="hljs-function">(<span class="hljs-params">sum, val</span>) =&gt;</span> 
            sum + <span class="hljs-built_in">Math</span>.pow(<span class="hljs-number">2</span>, -val), <span class="hljs-number">0</span>);
        <span class="hljs-keyword">return</span> (<span class="hljs-built_in">this</span>.m * <span class="hljs-built_in">this</span>.m) / harmonicMean;
    }
}
</code></pre>
<h3 id="heading-count-min-sketch-technical-details">Count-Min Sketch Technical Details</h3>
<pre><code class="lang-jsx"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CountMinSketch</span> </span>{
    <span class="hljs-keyword">constructor</span>(width, depth) {
        <span class="hljs-built_in">this</span>.width = width;
        <span class="hljs-built_in">this</span>.depth = depth;
        <span class="hljs-built_in">this</span>.table = <span class="hljs-built_in">Array</span>.from(
            { <span class="hljs-attr">length</span>: depth },
            <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(width).fill(<span class="hljs-number">0</span>)
        );
        <span class="hljs-built_in">this</span>.hashFunctions = <span class="hljs-built_in">this</span>.generateHashFunctions();
    }

    add(item, count = <span class="hljs-number">1</span>) {
        <span class="hljs-built_in">this</span>.hashFunctions.forEach(<span class="hljs-function">(<span class="hljs-params">hashFunc, i</span>) =&gt;</span> {
            <span class="hljs-keyword">const</span> j = hashFunc(item) % <span class="hljs-built_in">this</span>.width;
            <span class="hljs-built_in">this</span>.table[i][j] += count;
        });
    }

    estimate(item) {
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.min(
            ...this.hashFunctions.map(<span class="hljs-function">(<span class="hljs-params">hashFunc, i</span>) =&gt;</span> 
                <span class="hljs-built_in">this</span>.table[i][hashFunc(item) % <span class="hljs-built_in">this</span>.width]
            )
        );
    }
}
</code></pre>
<h3 id="heading-real-world-implementation-at-scale">Real-World Implementation at Scale</h3>
<pre><code class="lang-mermaid">graph TD
    A["Incoming Request"] --&gt; B["Edge Node"]
    B --&gt; C["HLL Counter"]
    B --&gt; D["Count-Min Sketch"]
    C &amp; D --&gt; E["Redis Cluster"]
    E --&gt; F["Rate Limit Decision"]
    style B fill:#96f,stroke:#333
    style E fill:#f96,stroke:#333
</code></pre>
<h3 id="heading-key-technical-considerations">Key Technical Considerations</h3>
<ul>
<li><p><strong>Memory Efficiency:</strong> Both algorithms use constant memory regardless of traffic volume</p>
</li>
<li><p><strong>Error Bounds:</strong> HLL provides ±2% error rate, Count-Min Sketch error is configurable</p>
</li>
<li><p><strong>Distributed System:</strong> Uses Redis CRDTs for counter synchronization across edge nodes</p>
</li>
<li><p><strong>Performance:</strong> O(1) time complexity for both updates and queries</p>
</li>
</ul>
<p><strong>By combining these probabilistic data structures, we can efficiently track and limit millions of requests per second, all while maintaining minimal memory usage and providing accurate rate limiting capabilities.</strong></p>
<h2 id="heading-real-world-use-cases">Real-World Use Cases</h2>
<p><strong>API Management:</strong> An API processing 10M requests per day uses CMS to identify abusive clients without tracking individual IPs.<br /><strong>E-commerce:</strong> HLL prevents coupon abuse by estimating unique users during flash sales.<br /><strong>Cybersecurity:</strong> The combination of both algorithms detects DDoS attacks with minimal memory overhead.</p>
<h2 id="heading-comparison-traditional-vs-probabilistic-approach">Comparison: Traditional vs. Probabilistic Approach</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Feature</td><td>Database Counters</td><td>HLL+CMS Approach</td></tr>
</thead>
<tbody>
<tr>
<td>Memory Usage</td><td>O(n)</td><td>O(1)</td></tr>
<tr>
<td>Distributed Sync</td><td>Complex</td><td><a target="_blank" href="https://tech.sedhu.me/conflict-free-replicated-data-types-crdts">CRDT-friendly</a></td></tr>
<tr>
<td>Error Margin</td><td>0%</td><td>1-2%</td></tr>
<tr>
<td>Throughput</td><td>10k RPS</td><td>1M+ RPS</td></tr>
</tbody>
</table>
</div><h3 id="heading-when-to-avoid-this-approach">When To Avoid This Approach</h3>
<ul>
<li><p>Banking transaction systems requiring exact counts</p>
</li>
<li><p>Legal compliance scenarios needing 100% accuracy</p>
</li>
<li><p>Systems with burst-spike allowances</p>
</li>
</ul>
<p>References:</p>
<p><a target="_blank" href="https://redis.io/docs/latest/develop/data-types/probabilistic/">https://redis.io/docs/latest/develop/data-types/probabilistic</a></p>
]]></content:encoded></item><item><title><![CDATA[Conflict-Free Replicated Data Types (CRDTs)]]></title><description><![CDATA["CRDT-friendly" describes systems, data structures, or operations designed to work seamlessly with Conflict-Free Replicated Data Types (CRDTs). These environments prioritize eventual consistency and support concurrent, decentralized updates without r...]]></description><link>https://tech.sedhu.me/conflict-free-replicated-data-types-crdts</link><guid isPermaLink="true">https://tech.sedhu.me/conflict-free-replicated-data-types-crdts</guid><category><![CDATA[CRDT]]></category><category><![CDATA[distributed system]]></category><category><![CDATA[#ConflictResolution]]></category><dc:creator><![CDATA[Sedhu]]></dc:creator><pubDate>Sun, 11 May 2025 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/uvTqhAnaf6s/upload/d6142cfac381581eef23015a28990196.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>"CRDT-friendly" describes systems, data structures, or operations designed to work seamlessly with <strong>Conflict-Free Replicated Data Types (CRDTs)</strong>. These environments prioritize <em>eventual consistency</em> and support concurrent, decentralized updates without requiring coordination between nodes. Here's a breakdown:</p>
<h2 id="heading-key-characteristics-of-crdt-friendly-systems">Key Characteristics of CRDT-Friendly Systems</h2>
<ol>
<li><p><strong>Decentralized Updates</strong></p>
<ul>
<li><p>Allow independent modifications across distributed replicas (e.g., collaborative text editors, IoT sensor networks).</p>
</li>
<li><p>Example: Multiple users editing the same document offline.</p>
</li>
</ul>
</li>
<li><p><strong>Mergeable Operations</strong></p>
<ul>
<li><p>Use commutative, associative, and idempotent operations to ensure conflict resolution.</p>
</li>
<li><p>Example: Incrementing counters (<code>counter += 1</code>) or adding elements to a set.</p>
</li>
</ul>
</li>
<li><p><strong>Eventual Consistency</strong></p>
<ul>
<li>Prioritize availability over strong consistency, tolerating temporary divergences (e.g., social media "likes" syncing across regions).</li>
</ul>
</li>
<li><p><strong>Scalable State Management</strong></p>
<ul>
<li>Optimize for minimal data transfer (e.g., delta-CRDTs sending only recent changes instead of full state)</li>
</ul>
</li>
</ol>
<h2 id="heading-crdt-friendly-data-structures-and-use-cases">CRDT-Friendly Data Structures and Use Cases</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Data Type</strong></td><td><strong>Use Case</strong></td><td><strong>Example Implementation</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Grow-only counters</td><td>Tracking website visits</td><td>TiDB’s <code>SUM(counter_value)</code></td></tr>
<tr>
<td>Last-write-wins registers</td><td>Configuration settings</td><td>Redis’s <code>SET</code> with timestamps</td></tr>
<tr>
<td>Add-only sets</td><td>Unique user ID storage</td><td>Riak’s CRDT-enabled sets</td></tr>
<tr>
<td>Collaborative text</td><td>Google Docs-style editing</td><td>TinyMCE’s per-character CRDTs</td></tr>
</tbody>
</table>
</div><h2 id="heading-systems-that-benefit-from-crdts">Systems That Benefit from CRDTs</h2>
<ul>
<li><p><strong>Distributed databases</strong> (e.g., Redis, Riak) for conflict-free replication.</p>
</li>
<li><p><strong>Collaborative apps</strong> (e.g., Figma, Apple Notes) supporting real-time sync.</p>
</li>
<li><p><strong>IoT networks</strong> handling offline data collection and delayed merges.</p>
</li>
</ul>
<h2 id="heading-non-crdt-friendly-scenarios">Non-CRDT-Friendly Scenarios</h2>
<ul>
<li><p><strong>Strong consistency requirements</strong> (e.g., banking transactions).</p>
</li>
<li><p><strong>Complex operations</strong> needing intent tracking (e.g., rich text formatting in TinyMCE).</p>
</li>
</ul>
<p>CRDT-friendly designs simplify distributed systems by eliminating consensus protocols, making them ideal for high-availability, low-latency applications</p>
]]></content:encoded></item><item><title><![CDATA[Blockchain, Ethereum - Getting started]]></title><description><![CDATA[This is the first article in my series about blockchains. 
Blockchain and its Basics
Blockchain, is a combination of a decentralized and distributed database containing a registry of transactions that are distributed among peers or fellow participant...]]></description><link>https://tech.sedhu.me/blockchain-ethereum-getting-started</link><guid isPermaLink="true">https://tech.sedhu.me/blockchain-ethereum-getting-started</guid><category><![CDATA[Blockchain]]></category><dc:creator><![CDATA[Sedhu]]></dc:creator><pubDate>Sun, 27 Feb 2022 15:03:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/hWJsOnaWTqs/upload/v1645971198725/ZyH4KXPyz.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is the first article in my series about blockchains. </p>
<h2 id="heading-blockchain-and-its-basics">Blockchain and its Basics</h2>
<p>Blockchain, is a combination of a decentralized and distributed database containing a registry of transactions that are distributed among peers or fellow participants in the network. The registry includes a long list of transactions and is continually updated with new transactions as they take place. Starting from the very first transaction, a bunch of transactions are grouped into a block as per a predefined block size (1 MB in case of Bitcoin). Once the block size is achieved by one block, the next set of transactions forms another block which is then linked to the block previously formed. Over time, a series of blocks is formed where each block is connected to another block that was created just before it. Thus, we call this chain of blocks the blockchain.</p>
<p>In the existing world, newspapers can be a loose example of where every piece of news is a transaction, Every day its new block and printing press is a block producer and every reader is a node.</p>
<p>To create a basic blockchain, following existing technologies and techniques were used.</p>
<ol>
<li>Digital Signatures </li>
<li>Hashing</li>
<li>Merkle Trees</li>
<li>Hash Cash</li>
<li>TCP/IP</li>
<li>P2P Network</li>
</ol>
<p><em>Cryptocurrency is a reward for node operators to run a blockchain node to cover their operating costs. Without this public chain can't exist.</em></p>
<h3 id="heading-digital-signatures">Digital Signatures</h3>
<p>A digital signature acts as a verifier of the authenticity of the sender.</p>
<p>There are two types of digital signatures:</p>
<h4 id="heading-symmetric-digital-signatures">Symmetric digital signatures:</h4>
<ul>
<li>One single key is used to encrypt the messages.</li>
<li>The sender encrypts the message with that key and sends it to the receiver.</li>
<li>Once the receiver receives it, they need the same key to decrypt or unlock that message.</li>
<li>So, the sender also shares the key he/she encrypted the message with so that the receiver can use it to decrypt the message.</li>
</ul>
<h4 id="heading-asymmetric-digital-signatures">Asymmetric Digital Signatures:</h4>
<ul>
<li>A pair of public and private keys are used.</li>
<li>A message encrypted with a public key can only be decrypted with the corresponding</li>
<li>Private key of the public-private key pair and vice versa.</li>
<li>The public key of each participant is shared across the network and the private key is held secret by the owner/sender.</li>
</ul>
<p>Additionally, in the case of normal web traffic, combination of both Asymmetric and Symmetric has been used to share the information between the browser and the server.
During the initial step, Asymmetric Digital Signatures are used to form a secure connection and then A Symmetric Key will be shared between both parties. This will help to reduce the overhead in subsequent traffic.</p>
<h3 id="heading-hashing">Hashing</h3>
<p>It is an encryption technique which is used to encrypt the data to ensure data security, and it's done using hash functions.
The hash function is a mathematical function that can take in any length of input and convert it to an output of fixed length.
The output hash serves like a fingerprint for that data.</p>
<p>Hash uniquely represents that data. Even a small change in the data generates an entirely random and different hash.</p>
<p><a target="_blank" href="https://www.2brightsparks.com/resources/articles/introduction-to-hashing-and-its-uses.html">Hashing &amp; its uses</a></p>
<p>This technique is used to pack transactions into block. So, if a bad actor tries to tamper with the existing transaction, it will result in hash output mismatch.</p>
<h3 id="heading-merkle-tree">Merkle Tree</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645970261947/tLFwptId9.png" alt="Hash_Tree.png" /></p>
<p>Merkel Tree is a way of organizing transactions in the block. The output of a Merkle tree is a Merkle root. The Merkle tree utilizes hash functions to arrive at the Merkle root by recursive hashing.</p>
<p>Even the smallest of changes in any of the data points will change its hash and hence, the root hash will also be changed. Thus, root hash is the fingerprint representing all the data points or block.</p>
<p>The Merkle trees help in verifying transaction data on a block by acting as a unique identifier for the transactions.</p>
<h3 id="heading-hash-cash">Hash Cash</h3>
<p>Hash cash is a proof of work the sender of an email must do before sending an email.
The sender must calculate the hash of the email data and make sure that the calculated hash satisfies a predefined condition. This limits the scope of spam emails.</p>
<p>The technique of hash cash is used in the process of creation of blocks in the blockchain. So that miner nodes are spending their resources (Computational power, memory, electricity etc.) to form a block.</p>
<h3 id="heading-tcpip">TCP/IP</h3>
<p>The TCP/IP is a communication protocol which sends data in the form of packets over a server using the IP addresses of the sender and receiver.</p>
<h3 id="heading-peer-to-peer-network">Peer to Peer Network</h3>
<p>In peer-to-peer (P2P) networking, a group of computers are linked together with equal permissions and responsibilities for processing data. Unlike traditional client-server networking, no devices in a P2P network are designated solely to serve or to receive data.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645970156506/I3TwlldHn.webp" alt="p2p-Copy.webp" /></p>
<p>In a peer-to-peer network, each device can act as a server and as a client at different points in time. In a blockchain peer-to-peer network, the peers use TCP/IP protocols to connect to other peers and transfer the data. </p>
<h2 id="heading-basic-architecture">Basic Architecture</h2>
<p>All the above-mentioned techniques to create the ideal business network, i.e., blockchain. The architecture of the blockchain network is as follows: </p>
<ul>
<li>There is a peer-to-peer network created between all the participants of the network.</li>
<li>Each network participant has a digital signature for identifying the participants Hash Cash TCP/IP and Peer to Peer Network Basic Architecture of Blockchain</li>
<li>Any transaction happening between two network participants gets flooded in the network with the help of gossip protocol to all the participants for validation.</li>
<li>Each participant has a transaction pool which is a memory space allocated to store the verified transactions.</li>
<li>Each transaction is secured using the hashing algorithm.</li>
<li>The transaction pool is at a node level.</li>
<li>There are designated nodes which create the blocks out of the transactions happening in the network at a frequency known as mining rate.</li>
<li>Once the block is created it gets flooded in the network and each network participant verifies the block of data and once there is a single source of truth for the block, the block gets added to the blockchain.</li>
</ul>
<h2 id="heading-consensus">Consensus</h2>
<p>Since public blockchain is an open source network, there should be a way for participants to mutually agree on the transactions and blocks in blockchain. So there is a need for a consensus layer.</p>
<p>Mathematical algorithms are used to verify transactions and ensure trust between transacting parties. Transacting parties have to trust the output achieved using these mathematical algorithms.</p>
<p>These algorithms together are known as the consensus mechanism in the blockchain. The consensus can for a transaction or for an entire block</p>
<p>There are many types of consensus algorithms; proof of work, proof of stake, practical BFT etc. Every blockchain network has formed its own consensus protocol based on their network needs.</p>
<h2 id="heading-data-immutability">Data Immutability</h2>
<p>Blockchain offers one more important feature that differentiates it from other networks: Data Immutability.</p>
<p>In practical terms, it refers to the extreme difficulty that one will face in trying to alter or make changes to the existing data.</p>
<p>Blockchain offers data immutability as follows:</p>
<ul>
<li>Blockchain forms a chain of blocks which are connected to each other via a link, also known as the previous hash pointer or simply the previous block hash.</li>
<li>The hash of a block is calculated for all the contents in the block header using a hashing algorithm.</li>
<li>Merkel root is a part of the block header and any change in any constituent transaction results in the change in the Merkel root.</li>
<li>Change in the Merkel root will result in changing the hash of the entire block.</li>
<li>If the hash of one block is changed, the block next to it will not have a link to this block as the previous hash will not match the new block.</li>
<li>Hence, the link between the blocks will be broken, and these blocks will become invalidated.</li>
</ul>
<p>Immutability is an important feature of the blockchain which helps protect data from being manipulated and also helps in identifying malicious nodes in the network.</p>
<p>Block header hash = SHA256 (previous block hash + Merkle root + timestamp + difficulty target + nonce) .</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645970407864/U1cdqcABS.png" alt="immutablity.png" /></p>
<p>*Challenges in protecting a blockchain network</p>
<p>51% attack: In case more than 51% of the nodes in the network are malicious the network could become unstable.*</p>
<h3 id="heading-bft-network">BFT Network</h3>
<p>For a network to be byzantine fault tolerant the number of malicious nodes in the network should be less than 1/3rd of the total nodes in the network. Whenever a node receives two conflicting messages, it goes for a majority vote and accepts the message which comes from majority number of nodes. To ensure that the correct message is accepted by the nodes in the network the total number of malicious nodes needs to less than 33.33% or 1/3rd of the network</p>
<p>   (3F+1) - Where is f is no of faulty nodes that can be present in the network</p>
<h2 id="heading-types-of-blockchain-networks">Types of Blockchain Networks</h2>
<ol>
<li>Public - Bitcoin, Ethereum<ul>
<li>Permissionless: A public permissionless blockchain is free for anyone to join or leave. This type of network provides anonymity, immutability and transparency but compromises on efficiency.Public<ul>
<li>Permissioned: A public permissioned blockchain is an intermediate between private and public networks. It values efficiency and immutability over transparency and anonymity, where every participating member is aware of the identities of the other members in the network.</li>
</ul>
</li>
</ul>
</li>
<li>Private / Enterprise - Hyperledger Fabric, Hyperledger Besu<ul>
<li>A private blockchain is one which is operated and managed by a single entity. These type of blockchains are generally applicable in the case of a conglomerate where the parent company runs the network for the underlying group of companies. In such a situation, they value efficiency over anonymity, transparency and immutability.</li>
</ul>
</li>
<li>Consortium<ul>
<li>A consortium blockchain is largely similar to a private blockchain but differs when you consider who controls or manages the network. Instead of concentrating all power in one entity, authority is distributed across two or more participants.<ol>
<li>Hybrid (Public + Private)</li>
</ol>
</li>
<li>It is used by some private solution providers where they roll up the transactions on their private chain and submit them to a public blockchain for integrity and data immutability.</li>
</ul>
</li>
</ol>
<h2 id="heading-bitcoin">Bitcoin</h2>
<p>It's a first generation Blockchain with a vision of open money. </p>
<h2 id="heading-ethereum">Ethereum</h2>
<p>Initially, the blockchain was used only for the transfer of values between multiple parties. Later, the blockchain community saw an opportunity to make advancements and that gave birth to smart contracts.</p>
<h2 id="heading-smart-contracts">Smart Contracts</h2>
<p>A smart contract is the digital version of an agreement contract that runs on the Blockchain nodes and is executed when a set of trigger criteria are met. If designed right, a smart contract will surely be successful as it is driven by a computer system without human intervention.</p>
<p>The Smart contract has evolved through the following phases:</p>
<ul>
<li><strong>State Machine</strong>: The very first version of the blockchain was a state machine. It was just like an accounting ledger, where it held only the state of the node.</li>
<li><strong>Smart Contracts</strong>: All the nodes within a computer network have a code associated with these nodes, which can run on its own based on predefined logic known as smart contracts.</li>
<li><strong>Oracles</strong>: Oracles are external agents which connect the smart contract to the external world. Any external data that is required for the smart contract will be fetched by agents called oracles.</li>
</ul>
<h3 id="heading-introduction">Introduction</h3>
<p>Ethereum is a decentralised, open-source, distributed computing platform that enables the creation of smart contracts and decentralised applications, also known as Dapps.</p>
<p>It is essentially a transaction-based state machine. State machine refers to something that would read from a series of inputs and then transition to a new state based on those inputs.</p>
<p>The Ethereum blockchain starts from the ‘genesis state’, which is the first state. And after every transaction, the state of the entire blockchain changes.</p>
<p>https://ethereum.org/en/whitepaper/</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645972218510/6IPWxto0k.png" alt="image.png" /></p>
<h3 id="heading-basics">Basics</h3>
<h4 id="heading-ether">Ether</h4>
<ul>
<li>Ether is the main currency on the Ethereum blockchain.</li>
<li>It is a form of payment made by the client of the platform to the machine that executes its requested operation.</li>
<li>Every time a block is validated, 5 Ethers are created and awarded to the successful node.</li>
<li>1 Ether = 1018 wei (Smallest Unit)</li>
<li>1 Gwei = 109 wei</li>
</ul>
<h4 id="heading-gas">Gas</h4>
<ul>
<li>Gas is a unit to measure the fee required for a computation.</li>
<li>It refers to the fee required to successfully perform a transaction or execute a contract on the Ethereum blockchain</li>
<li>The exact price of gas is determined by the network’s miners, who can decline from processing a transaction if the gas price does not meet their threshold.</li>
</ul>
<h4 id="heading-gas-price">Gas Price</h4>
<ul>
<li>Gas price is the amount of Ether paid per unit of Gas spent by a miner for running a transaction</li>
</ul>
<h4 id="heading-gas-limit">Gas Limit</h4>
<p> Gas limit is the maximum amount of Gas required to run a transaction.</p>
<p>Transaction limit is the maximum amount that the sender would need to spend in order to perform a transaction successfully.</p>
<p><em>Transaction Limit = Gas Limit X Gas Price</em></p>
<p>Gas price and Gas limit are set by the sender of a transaction.</p>
<p>The system would deduct this amount from the sender based on the amount of Gas consumed in running the transaction.</p>
<h4 id="heading-accounts">Accounts</h4>
<p>In Ethereum, the state is made up of objects called "accounts", with each account having a 20-byte address and state transitions being direct transfers of value and information between accounts. An Ethereum account contains four fields:</p>
<ul>
<li>The nonce, a counter used to make sure each transaction can only be processed once</li>
<li>The account's current ether balance</li>
<li>The account's contract code, if present</li>
<li>The account's storage (empty by default)</li>
</ul>
<p>Accounts in Ethereum are stored in a global ‘shared state’ in the form of key–value pairs.</p>
<p>The list of all these key–value pairs representing accounts defines the state of Ethereum at that point.</p>
<p>Ethereum has two types of accounts:</p>
<ol>
<li>Externally owned accounts (EOAs) </li>
<li>Contract accounts (CAs).</li>
</ol>
<h5 id="heading-externally-owned-accounts-eoas">Externally Owned Accounts (EOAs):</h5>
<p>These are combinations of public addresses and private keys, and there is no code associated with them. </p>
<ul>
<li>Send and receive Ether to/from another account,</li>
<li>Send transactions to smart contracts.</li>
</ul>
<h5 id="heading-contract-accounts-cas">Contract Accounts (CAs):</h5>
<p>These accounts do not have a corresponding private key. These accounts are generated when you deploy your contract on blockchain. You will see them referred to as just contracts</p>
<ul>
<li>Send and receive Ether just like EOAs.</li>
<li>Unlike EOAs, they have code associated with them. </li>
<li>Transactions have to be triggered by an EOA or another contract.</li>
</ul>
<p>In a key–value pair, the key is a 20-byte string, which is usually the public address of the account.</p>
<p><strong>Nonce</strong>: For EOAs, nonce is the number of transactions that are sent from an account’s address. For CAs, nonce is the number of contracts created by the account. </p>
<p><strong>Balance</strong>: It is calculated as Number of wei/Ether owned by an account.</p>
<p><strong>Storage Root (Storage Hash)</strong>: This is a hash of the root node of a Merkle tree that encodes the hash of the storage content of an account.</p>
<p><strong>Code Hash</strong>: This is the hash of the EVM (Ethereum Virtual Machine) code of this account</p>
<p><strong> State Root</strong></p>
<p>Whenever a new block is created, a state root is stored in the header of that block. State root is the Merkle root of all the accounts at that moment. Simply put, all the key–value pairs that represent an account together, form a Merkle tree is known as state root. </p>
<p>This state root is captured by a block at the time of its creation. Any change in the data would lead to the calculation of the Merkle tree all over again to match the state root in the block.</p>
<p>This is highly impossible, and hence the immutability is maintained in the network. Along with state root, the transaction root and the receipt root are also used to capture the state of the network in every block.  By calculating and storing the roots mentioned above, Ethereum captures the network state every time a new block is created (state root, transaction root and receipt root).</p>
<h3 id="heading-transactions">Transactions</h3>
<p>Interaction between accounts are called transactions. Its a signed data package that stores a message that is to be sent from an EOA to another account on the blockchain.</p>
<p>The following points summaries the transactions in Ethereum: </p>
<ul>
<li>An EOA can send messages to other EOAs or to other CAs by creating and signing a transaction using its private key. </li>
<li>A message between two EOAs is simply a value transfer. </li>
<li>But a message from an EOA to a CA activates the CA’s code, allowing it to perform various actions (e.g., transfer tokens, write to internal storage, mint new tokens, perform some calculation, create new contracts, etc.). </li>
<li>Unlike EOAs, CAs cannot initiate new transactions on their own. </li>
<li>Instead, CAs can only fire transactions in response to other transactions that they have received (from an EOA or from another CA).</li>
<li>An action that occurs on the Ethereum blockchain is always set in motion by transactions that are fired from EOAs.</li>
</ul>
<p>Note: Transactions can be triggered from an EOA only.
<strong>Components of a Transaction</strong>
<strong>Nonce</strong>: A count of the number of transactions sent by the sender</p>
<p><strong>Gas Price</strong>: The amount of Wei that the sender is willing to pay per unit of Gas required to execute the transaction</p>
<p><strong>Gas Limit</strong>: The maximum amount of gas that the sender is willing to pay to execute a transaction</p>
<p><strong>to</strong>: The address of the recipient. In a contract-creating transaction, an empty value is used</p>
<p><strong>value</strong>: The amount of Wei to be transferred from the sender to the recipient. In a contract-creating transaction, this value serves as the starting balance within the newly created contract account </p>
<p><strong>v, r, s</strong>: Used to generate the signature that identifies the sender of the transaction </p>
<p><strong>init</strong>: An EVM code fragment that is used to initialise the new contract account </p>
<p><strong>data</strong>: The input data (i.e., parameters) of the message call. Each type of transaction has all the components above
Types of Transaction</p>
<p><strong>Message Calls</strong>: These are internal transactions between an EOA and a CA or between one CA and another. When one CA sends a message to another CA, the associated code that exists on the recipient CA is executed.</p>
<p><strong>Value Transfer</strong>: These are transfer of value from one EOA to another. </p>
<p><strong>Contract Creation Calls</strong>: They are initiated by EOAs, and the recipient’s address is kept empty. Such transactions create a new CA and, hence, are used to create and install new Ethereum contracts</p>
<h3 id="heading-block">Block</h3>
<p>Blocks are batches of transactions with a hash of a previous block in the chain</p>
<p>A block consists of</p>
<ul>
<li>Header</li>
<li>List of Transactions</li>
<li>Headers Hash of Ommer Block</li>
<li>Mining details</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645972840331/MPr9jjtoI.png" alt="image.png" /></p>
<p><strong> Consensus in Ethereum</strong></p>
<p>Ethereum is currently operating on a Proof-of-Work (PoW) consensus protocol, but in the future, it will be shifting to a Proof-of-Stake (PoS) protocol.</p>
<p>The current Ethereum blockchain uses a consensus algorithm called Ethash, which is built specifically for the Ethereum blockchain.</p>
<p>The <strong>Ethash</strong> PoW algorithm introduces the property of ‘memory hardness’ to the Ethereum blockchain.</p>
<p>Ethereum made its mining process highly I/O intensive. This is done with the help of a DAG (directed acyclic graph) – a very large file that is passed along with a block as an input to the hashing algorithm, Ethash.</p>
<p>The DAG requires sufficient memory size, and so, the entire hashing process is a CPU-based rather than a GPU-based process.</p>
<p>Therefore, any system with memory large enough to hold a DAG can now stand a chance to mine the block successfully. And this is why Ethereum has a 15-second block creation rate, as compared with 10 minutes for Bitcoin.</p>
<p><strong>Forks</strong>
Forks can be classified as accidental or intentional.</p>
<p><strong>Soft Fork</strong></p>
<p>An accidental fork is created when two or more miners find a block at nearly the same time.</p>
<p>The fork is resolved when subsequent block(s) are added and one of the chains becomes longer than the alternative chain(s).</p>
<p>The network abandons the blocks that are not in the longest chain (they are called orphaned blocks).</p>
<p><strong>Ommer Block</strong> Ommer/uncle blocks are created in Ethereum blockchains when two blocks are mined and submitted to the ledger at roughly the same time. Only one can enter the ledger as a block, while the other does not. They are similar to Bitcoin orphans but have an integrated use, unlike their Bitcoin counterparts. The orphan block is called the Ommer block</p>
<p> <strong>Hard Fork</strong></p>
<p>A hard fork is a permanent split of a blockchain, and it is backward-incompatible.</p>
<p>It divides a blockchain into two separate chains, both of which are dominant.</p>
<p>A hard fork is generally a change in protocol that renders the older version invalid.</p>
<p>Ex: Ethereum and Ethereum Classic</p>
<h3 id="heading-transaction-flow">Transaction Flow</h3>
<p><strong>Logs and Receipts</strong></p>
<p>Ethereum maintains logs to track various transactions and messages.</p>
<p>A contract can explicitly generate a log by defining events.</p>
<p>A log entry contains: </p>
<p> 1.The account address,</p>
<ol>
<li>Events </li>
<li>Data.</li>
</ol>
<p>A log stored in the header comes from the log information present in the transaction receipt.</p>
<p>Ethereum generates a receipt for every transaction.</p>
<p>These receipts include:</p>
<ol>
<li>Block number, </li>
<li>Block hash,</li>
<li>Transaction hash,</li>
<li>Gas used,</li>
<li>Cumulative gas used in the block </li>
<li>Logs of current transaction</li>
</ol>
<p><strong>State Transition</strong></p>
<p>Ethereum is a State Transition Machine, which is a process that takes the system from an existing state (N1) to a new state (N2) after a transaction is given as an input to the network.</p>
<p>Ethereum keeps moving between these states as new transactions keep occurring on the network.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645973258488/cFL5TnNxP.png" alt="image.png" /></p>
<p>Whenever a transaction is triggered by an EOA, that transaction has to go through certain checks and a specific process as follows</p>
<ol>
<li>valid signature</li>
<li>valid transaction / sender account nonce</li>
<li>gas limit</li>
<li>sender account balance</li>
</ol>
<p>Once all parameters are valid, the transaction gets executed as follows</p>
<ul>
<li>Upfront cost of execution is deducted from the sender’s balance</li>
<li>Nonce of the sender’s account is increased by 1 to account for the current transaction</li>
<li>When the transaction starts executing. Throughout the execution, Ethereum keeps track of the ‘substate’, which is a way to record information accrued during the transaction that would be needed immediately after the transaction is complete</li>
<li>A self-destruct set, which is a set of accounts (if any) that would be discarded after the transaction completes</li>
<li>Log series, which are archived and indexable checkpoints of the virtual machine’s code execution</li>
<li>Refund balance, which is the amount to be refunded to the sender’s account after the transaction</li>
<li>The various computations required by the transaction are processed.</li>
<li>Once all the steps required by the transaction have been processed, and assuming there is no invalid state, the state is finalised by determining the amount of unused gas to be refunded to the sender</li>
</ul>
<p><strong>Tokens</strong></p>
<p>Ethereum tokens are simply digital assets with standards that are being built on top of the Ethereum blockchain. They benefit from Ethereum’s existing infrastructure instead of developers having to build an entirely new blockchain.</p>
<p>Ethereum is a platform that can be used to create any arbitrary smart contract including smart contracts that represent digital assets called Ethereum tokens.</p>
<p>These token standards are called ERC standards. </p>
<p>ERC stands for Ethereum Request for Comments. These are application-level comments or standards defined for the token. Anyone can create an ERC. However, the ERC’s author must include a clear explanation of its standard. </p>
<p>Applications can use this common ERC token and make it easier to interact with each other. The conditions of the standard can include anything; for example, the way it will interact with a smart contract.</p>
<p>Some of the common ERC standards are ERC 20, ERC 721 and ERC 777 etc.
<strong>Fungible and Non-Fungible Tokens</strong>
<strong>Fungible tokens </strong></p>
<ul>
<li>These are tokens with identical properties.</li>
<li>One token can be interchanged with another token of the same value.</li>
<li>Tokens are divisible into smaller amounts as long as they add up to the same</li>
<li>Some of the examples of fungible token standards are ERC 20, ERC 777 and ERC 223.</li>
</ul>
<p><strong>ERC - 20</strong></p>
<ul>
<li>ERC 20 is an asset created on Ethereum and is written in Solidity.</li>
<li>It is hosted on the Ethereum blockchain. </li>
<li>ERC 20 tokens are stored and sent using Ethereum addresses and transactions. </li>
<li>This standard has six functions – totalSupply, balanceOf, transfer, transferFrom, approve and allowance.</li>
<li>These functions can be used to transfer tokens to an address or a smart contract.</li>
</ul>
<p><strong>ERC 777
</strong></p>
<ul>
<li>It is similar to ERC 20 with extra functionalities like mint and burn.</li>
</ul>
<p><strong>Non-Fungible Tokens</strong></p>
<ul>
<li>These are tokens with unique properties. Even if two tokens are of the same type, they are unique</li>
<li>No two tokens can be interchanged as they all have unique specifications.</li>
<li>As these tokens can represent an identity, they are not divisible into smaller units.</li>
</ul>
<p><strong>ERC - 721</strong></p>
<ul>
<li>Every non-fungible token (like ERC 721) has a metadata associated with it which defines the token.</li>
<li>This metadata is unique for each token. For example, suppose you have a land registry document set. Each document represents one token.</li>
<li>Creating a unique set of tokens is also known as tokenizing assets on the blockchain network where each document can represent an asset with certain properties</li>
</ul>
<p><strong>Challenges in public ethereum</strong></p>
<ul>
<li>Scalability/throughput: The number of transactions per second is very low</li>
<li>Cost: The mining process is expensive and transaction gas fees is also very high</li>
<li>Size: The size of the network is increasing, and nodes have to be highly equipped in order to store a blockchain of this size</li>
</ul>
<p><strong>Merkle Patricia Trie</strong></p>
<p>Ethereum stores data in both Merkle trees and a radix tree called Patricia trie.</p>
<ul>
<li>Ethereum combines the properties of a Merkle tree and a Patricia trie to create a modified Merkle Patricia trie.</li>
<li>A Merkle tree can be used only to check whether a value is present in the tree or not. </li>
<li>A Patricia trie (a radix tree) is a data structure that stores values in key–value pairs. This is optimum in finding common prefixes of the searched value.</li>
<li>These combined properties of both the trees yields a structure that is more optimum for storing and checking values than Merkle tree individual.
Ethereum uses a database to store its trie structure. Some examples of such databases could include LevelDb and RocksDB. The purpose of storing this data is to go forward and backward, and identify the exact data that was tampered with.</li>
</ul>
<p>In the next article, I will write about Applications of Ethereum. </p>
]]></content:encoded></item><item><title><![CDATA[Hacking a Website with just a backlink]]></title><description><![CDATA[Your website can be hacked if it has a backlink to a malicious website.
Let's say you have added a backlink to a great website for whatever reason. But for some reason, they closed the shop and that domain was available for sale OR somehow a hacker g...]]></description><link>https://tech.sedhu.me/hacking-a-website-with-just-a-backlink</link><guid isPermaLink="true">https://tech.sedhu.me/hacking-a-website-with-just-a-backlink</guid><category><![CDATA[Security]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[frontend]]></category><dc:creator><![CDATA[Sedhu]]></dc:creator><pubDate>Sun, 20 Feb 2022 12:57:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1645360041263/DOITQ8pi1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-your-website-can-be-hacked-if-it-has-a-backlink-to-a-malicious-website">Your website can be hacked if it has a backlink to a malicious website.</h3>
<p>Let's say you have added a backlink to a great website for whatever reason. But for some reason, they closed the shop and that domain was available for sale OR somehow a hacker got control of that domain. </p>
<p>The screenshot of expired domains shows that there are many domains with millions of backlinks to them. If a hacker gets hold of some domain which has a backlink from a legitimate site, and Boom !! </p>
<p>Their users are vulnerable to this kind of hack. Even a simple email ID theft can also cause larger damages. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1645360896038/3O6wy1Fsp.png" alt="image.png" /></p>
<p><strong>
Really !! How? </strong></p>
<p>This is interesting. When a page is opened via a backlink provided it has the context of parent page via <code>window.opener</code> property. </p>
<p>That malicious website can do anything like</p>
<ol>
<li>Accessing the cookies 
<code>window.opener.document.cookie</code> will get access to all the cookies</li>
<li>Manipulating the DOM</li>
<li>Replace it completely with a similar page and do Phishing !! 
<code>window.opener.location = www.something-similar.com</code> </li>
</ol>
<p>The Options are endless. </p>
<h3 id="heading-ugh-how-to-fix-it">Ugh !! How to fix it?</h3>
<p>To prevent it from happening, when adding a backlink you should also add rel='noopener'</p>
<pre><code><span class="hljs-operator">&lt;</span>a href<span class="hljs-operator">=</span><span class="hljs-string">"external-site.com"</span> target<span class="hljs-operator">=</span><span class="hljs-string">"_blank"</span> rel<span class="hljs-operator">=</span><span class="hljs-string">"noopener"</span><span class="hljs-operator">&gt;</span>Reference<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>a<span class="hljs-operator">&gt;</span>
</code></pre><p><strong>How did this work? </strong></p>
<p>It removes the reference to the parent page. When the malicious website tries to access it, it returns 'null' and your website is saved from those prying eyes of hacker. </p>
<p>To know more about all the options for the 'rel' property </p>
<p>https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel</p>
]]></content:encoded></item><item><title><![CDATA[Don't use util.promisify]]></title><description><![CDATA[Many developers use util.promisify() without hesitation when they need to convert the callback method to a promise based method.
But there is a catch. util.promisify() changes this context to golbalThis. So if the actual method is using this somewher...]]></description><link>https://tech.sedhu.me/dont-use-utilpromisify</link><guid isPermaLink="true">https://tech.sedhu.me/dont-use-utilpromisify</guid><category><![CDATA[promises]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Sedhu]]></dc:creator><pubDate>Tue, 06 Jul 2021 08:29:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1625560147703/bZHjuQonh.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Many developers use <code>util.promisify()</code> without hesitation when they need to convert the callback method to a promise based method.</p>
<p>But there is a catch. <code>util.promisify()</code> changes <code>this</code> context to <code>golbalThis</code>. So if the actual method is using <code>this</code> somewhere in the code, it will fail/produce undesired results.</p>
<pre><code class="lang-javascript">mailer.sendSMTP = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">message, cb</span>)</span>{
        <span class="hljs-comment">// some code </span>
        <span class="hljs-built_in">this</span>.smtpTransport.sendMail(message , cb);
    };
</code></pre>
<p>The above code will work perfectly fine when it is invoked with a callback fn. But when its promised as follows then it will throw <code>TypeError: Cannot read property 'sendMail' of undefined</code></p>
<pre><code class="lang-javascript"> <span class="hljs-keyword">try</span> {
        emailResult = <span class="hljs-keyword">await</span> promisify(mailer.sendSMTP)(emailToSend);
    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-keyword">throw</span> <span class="hljs-string">`Error while sending email: <span class="hljs-subst">${error}</span>`</span>;
    }
</code></pre>
<p>it can be fixed either by binding the context again or changing the implementation.</p>
<p>Binding the context using <code>call</code>, <code>apply</code> or <code>bind</code> methods</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">try</span> {
        emailResult = <span class="hljs-keyword">await</span> promisify(mailer.sendSMTP).call(mailer,emailToSend);
    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-keyword">throw</span> <span class="hljs-string">`Error while sending email: <span class="hljs-subst">${error}</span>`</span>;
    }
</code></pre>
<p>Moving the context variable outside of the method.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> _that = mailer; 
mailer.sendSMTP = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">message, cb</span>)</span>{
        <span class="hljs-comment">// some code </span>
        _that.smtpTransport.sendMail(message , cb);
    };
</code></pre>
<p>References: https://github.com/nodejs/node/issues/13338</p>
]]></content:encoded></item><item><title><![CDATA[Terminal Tricks and Shortcuts - Hidden Gems!]]></title><description><![CDATA[As a developer, most of our time is spent on the terminal. We get things done with our known set of commands mostly. 
But there are so many hidden gems inside our terminal that will elevate our skills and productivity. 
In this series part 1, I'm gon...]]></description><link>https://tech.sedhu.me/terminal-tricks-and-shortcuts-hidden-gems</link><guid isPermaLink="true">https://tech.sedhu.me/terminal-tricks-and-shortcuts-hidden-gems</guid><category><![CDATA[terminal]]></category><category><![CDATA[software development]]></category><category><![CDATA[Developer]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Sedhu]]></dc:creator><pubDate>Thu, 17 Jun 2021 17:01:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1623939395400/FckV2dsJ8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a developer, most of our time is spent on the terminal. We get things done with our known set of commands mostly. </p>
<p>But there are so many hidden gems inside our terminal that will elevate our skills and productivity. </p>
<p>In this series part 1, I'm gonna share a few things that will work for all the commands. </p>
<p>Most of the times we work inside the terminal in following patterns</p>
<ol>
<li>Same Command with different arguments</li>
<li>Different command with the same arguments </li>
</ol>
<p>In this case, we either retype the whole thing or edit the previous command/input. With the following set of tricks, we can do better. </p>
<pre><code> ! <span class="hljs-keyword">is</span> the <span class="hljs-keyword">operator</span> which <span class="hljs-keyword">is</span> going <span class="hljs-keyword">to</span> help us <span class="hljs-keyword">in</span> these scenarios

 ⇄  Tab <span class="hljs-keyword">Or</span> ↵ enter can be used <span class="hljs-keyword">to</span> replace these substitutions
</code></pre><h2 id="same-command-with-different-arguments">Same Command with different arguments</h2>
<h3 id="operator-substitues-last-command">!! Operator - Substitues Last Command</h3>
<p>Many times we would have run to a scenario where admin rights are needed for command.</p>
<pre><code>&gt;cat <span class="hljs-keyword">system</span>.<span class="hljs-keyword">log</span>
You need administrator <span class="hljs-keyword">access</span>
</code></pre><p>and then we add <code>sudo</code> and type the command again. But there is a better way, type <code>sudo !!</code> and <code>enter ↵</code> or <code>⇄ Tab</code></p>
<pre><code>&gt; sudo !! 
sudo cat <span class="hljs-keyword">system</span>.<span class="hljs-keyword">log</span>

&lt;<span class="hljs-keyword">log</span> file content&gt;
</code></pre><p>We can also add <code>alias su='su !!'</code> to our profile</p>
<h3 id="0-previous-command-without-arguments">!:0 - Previous Command without arguments</h3>
<p>To repeat the previous command with a different argument then type <code>!:0</code> and press ↵ enter that will insert the previous command without the arguments and modifiers </p>
<pre><code><span class="hljs-quote">&gt; cat abc.txt</span>
<span class="hljs-quote">&gt; !:0 (↵ enter) </span>
<span class="hljs-quote">&gt; cat |</span>
</code></pre><p>To run it with some parts then <code>!:0-n</code> will get till the nth part of it</p>
<pre><code>
<span class="hljs-string">&gt;</span> <span class="hljs-string">ssh</span> <span class="hljs-string">-i</span> <span class="hljs-string">dev-uswest.pem</span> <span class="hljs-string">ubuntu@10.801.118.256</span>
    <span class="hljs-number">0</span>  <span class="hljs-number">1</span>  <span class="hljs-number">2</span>              <span class="hljs-number">3</span>

<span class="hljs-string">&gt;</span> <span class="hljs-type">!:0</span><span class="hljs-number">-2</span>  <span class="hljs-string">(↵</span> <span class="hljs-string">enter)</span>

<span class="hljs-string">&gt;</span> <span class="hljs-string">ssh</span> <span class="hljs-string">-i</span> <span class="hljs-string">dev-uswest.pem</span> <span class="hljs-string">|</span>
</code></pre><h3 id="prefix-repeat-previous-command-that-starts-with-a-specific-wordletter">!prefix - Repeat previous command that starts with a specific word/letter</h3>
<p>To repeat the last command which starts with <code>ssh</code> then</p>
<pre><code>&gt;!<span class="hljs-selector-tag">ssh</span> (↵ <span class="hljs-selector-tag">enter</span>)
&gt;<span class="hljs-selector-tag">ssh</span> <span class="hljs-selector-tag">-i</span> <span class="hljs-selector-tag">uswest</span><span class="hljs-selector-class">.pem</span> <span class="hljs-selector-tag">ubuntu</span><span class="hljs-keyword">@10</span>.01.118.256
</code></pre><h3 id="str-last-command-containing-str">!?str? - Last command containing str</h3>
<p>To repeat the command which contains <code>sedhu</code> from the history</p>
<pre><code><span class="hljs-quote">&gt; !?sedhu?</span>
<span class="hljs-quote">&gt; ssh -i uswest.pem sedhu@10.801.118.256</span>
</code></pre><h3 id="ctrl-r-reverse-search">Ctrl + R Reverse Search</h3>
<p>Search through the commands history and Keep Pressing <code>Ctrl + R</code> and <code>Ctrl+Shift+R</code> to continue searching Forward and Backward. </p>
<pre><code>
<span class="hljs-string">~/.aws</span> <span class="hljs-string">⌚</span> <span class="hljs-number">21</span><span class="hljs-string">:31:24</span>
<span class="hljs-string">$</span> <span class="hljs-string">ssh</span> <span class="hljs-string">-i</span> <span class="hljs-string">uswest.pem</span> <span class="hljs-string">ubuntu@100.81.112.257</span>
<span class="hljs-attr">bck-i-search:</span> <span class="hljs-string">ssh_</span>
</code></pre><h3 id="n-nth-command-from-the-history">!n - Nth command from the history</h3>
<p>If we remember the command <code>history</code> position, we use <code>!n</code> and <code>!-n</code> to execute the command in position from Top and Bottom of the list. </p>
<p>If the history is as follows</p>
<pre><code><span class="hljs-string">&gt;</span> <span class="hljs-string">history</span>
    <span class="hljs-number">1</span>  <span class="hljs-string">git</span> <span class="hljs-string">push</span> <span class="hljs-string">-f</span> <span class="hljs-string">--no-verify</span>
    <span class="hljs-number">2</span>  <span class="hljs-string">npmR</span> <span class="hljs-string">test</span>
    <span class="hljs-number">3</span>  <span class="hljs-string">npmR</span> <span class="hljs-string">start:dev</span>
    <span class="hljs-number">4</span>  <span class="hljs-string">npmR</span> <span class="hljs-string">test</span>
</code></pre><p>then results of such commands will be</p>
<pre><code>&gt; !<span class="hljs-selector-tag">1</span> <span class="hljs-comment">// git push -f --no-verify</span>
&gt; !<span class="hljs-selector-tag">-1</span> <span class="hljs-comment">// npmR test</span>
&gt; !<span class="hljs-selector-tag">-2</span> <span class="hljs-comment">// npmR start:dev</span>
</code></pre><h2 id="different-command-with-the-same-arguments">Different command with the same arguments</h2>
<h3 id="dollar-substitutes-last-argument">!$ Substitutes last argument</h3>
<p>Let's say we were viewing a config file with the <code>cat</code> command </p>
<pre><code>/etc/vim ⌚ 15:12:23
$ cat vimrc 
<span class="hljs-tag">&lt;<span class="hljs-name">vimrc</span> <span class="hljs-attr">file</span> <span class="hljs-attr">content</span>&gt;</span>
</code></pre><p>now we need to edit something in that, type <code>vi !$</code> and ↵ Enter</p>
<pre><code>/etc/vim ⌚ <span class="hljs-number">15</span><span class="hljs-symbol">:</span><span class="hljs-number">12</span><span class="hljs-symbol">:</span><span class="hljs-number">31</span>
$ vi !$ (↵ enter)

/etc/vim ⌚ <span class="hljs-number">15</span><span class="hljs-symbol">:</span><span class="hljs-number">14</span><span class="hljs-symbol">:</span><span class="hljs-number">50</span>
$ vi vimrc
</code></pre><h4 id="esc-dot-inserts-last-argument">Esc . (Dot) - Inserts last argument</h4>
<p>Suppose we need to inspect before executing that command then type <code>Esc .</code> which will insert the argument </p>
<pre><code>/etc/vim ⌚ <span class="hljs-number">15</span><span class="hljs-symbol">:</span><span class="hljs-number">15</span><span class="hljs-symbol">:</span><span class="hljs-number">32</span>
$ vi (Esc .) 
$ vi vimrc
</code></pre><h3 id="few-more">Few more</h3>
<ul>
<li><code>!^</code> - Substitutes first argument from the previous command</li>
<li><code>!*</code> - Sustitues all arguments from the previous command</li>
<li><code>!n</code> -  Nth Argument from the previous command</li>
<li><code>!-n</code> -  Nth Argument from the previous command (Backwards)</li>
<li><code>^val1^val2</code> -  Last command replacing val1 for val2</li>
</ul>
<pre><code><span class="hljs-quote">&gt; ssh -i uswest.pem ubuntu@10.81.118.219</span>
<span class="hljs-quote">&gt; ^ubuntu^sedhu (↵ enter)</span>
<span class="hljs-quote">&gt; ssh -i uswest.pem sedhu@10.81.118.219</span>
</code></pre>]]></content:encoded></item><item><title><![CDATA[IPv6 Mask / Prefix Conversion]]></title><description><![CDATA[In our SDWAN solution, I have just started to implement IPv6 Support for the upcoming release. 
For all IP validations I'm using ip-address.js NPM library, however it doesn't have all the functionalities. 
So I had to extend it and build our own NPM ...]]></description><link>https://tech.sedhu.me/ipv6-mask-prefix-conversion</link><guid isPermaLink="true">https://tech.sedhu.me/ipv6-mask-prefix-conversion</guid><category><![CDATA[networking]]></category><category><![CDATA[internet]]></category><dc:creator><![CDATA[Sedhu]]></dc:creator><pubDate>Tue, 01 Jun 2021 07:31:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1631773777498/sBTmmj_kC.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In our SDWAN solution, I have just started to implement IPv6 Support for the upcoming release. 
For all IP validations I'm using <a target="_blank" href="http://ip-address.js.org/">ip-address.js</a> NPM library, however it doesn't have all the functionalities. </p>
<p>So I had to extend it and build our own NPM library so that it could be consumed by our products. </p>
<p>One of the main functionality which was missing was prefix - mask conversion and validation. As usual, I googled it, Unfortunately I didn't find any solution. So I had to implement it on my own and i thought it could be useful for the community.</p>
<h3 id="prefix-to-mask-conversion">Prefix to Mask Conversion</h3>
<p>Input will be a number between 0 and 128 and the function should return the corresponding IPv6 Mask. </p>
<h4 id="explanation">Explanation:</h4>
<ol>
<li>Create a 128 bit binary string</li>
<li>Fill the first n (prefix) bits with 1s and the remaining with 0s. </li>
<li>Chunk them with a size of 16 bits each</li>
<li>Convert each chuck to a hex value and join them with (:) </li>
</ol>
<p>For Example: 
<code>118 =&gt; 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:fc00'</code> </p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">const</span> HEX = <span class="hljs-number">16</span>;
<span class="hljs-keyword">const</span> BINARY = <span class="hljs-number">2</span>;
<span class="hljs-keyword">const</span> MAX_PREFIX = <span class="hljs-number">128</span>;
<span class="hljs-keyword">const</span> MIN_PREFIX = <span class="hljs-number">0</span>;

  <span class="hljs-keyword">static</span> isValidPrefix(prefix: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">boolean</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">Number</span>.isInteger(prefix) &amp;&amp; prefix &gt;= MIN_PREFIX &amp;&amp; prefix &lt;= MAX_PREFIX;
  }
  <span class="hljs-comment">/**
   *
   * @param prefix
   * @returns ipv6 netmask address
   *
   * Fill an array with 1s for given number of prefix bits
   * Fill the remaining bits with 0s
   * chunk it with 16 elements and covert each chunk to hex
   *
   */</span>
  <span class="hljs-keyword">static</span> getNetmaskForPrefix(prefix: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">string</span> {
    <span class="hljs-keyword">if</span> (!IPV6.isValidPrefix(prefix)) {
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(<span class="hljs-string">'Invalid IPv6 Prefix'</span>);
    }

    <span class="hljs-keyword">const</span> prefixArr: <span class="hljs-built_in">number</span>[] = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(prefix).fill(<span class="hljs-number">1</span>);
    <span class="hljs-keyword">const</span> chunkArr = <span class="hljs-built_in">Array</span>.from({
      length: <span class="hljs-built_in">Math</span>.ceil(prefixArr.length / HEX),
    }, <span class="hljs-function">(<span class="hljs-params">_v, i</span>) =&gt;</span> prefixArr.slice(i * HEX, i * HEX + HEX));

    <span class="hljs-comment">// Converting from binary to hex</span>
    <span class="hljs-keyword">let</span> subnet = chunkArr.map(<span class="hljs-function">(<span class="hljs-params">item</span>) =&gt;</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-built_in">parseInt</span>(item.join(<span class="hljs-string">''</span>).padEnd(HEX, <span class="hljs-string">'0'</span>), BINARY).toString(HEX);
    }).join(<span class="hljs-string">':'</span>);

    <span class="hljs-keyword">if</span> (subnet.length &lt; <span class="hljs-number">35</span>) {
      subnet = <span class="hljs-string">`<span class="hljs-subst">${subnet}</span>::`</span>;
    }

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Address6(subnet).canonicalForm();
  }
</code></pre>
<h3 id="mask-to-prefix-conversion">Mask to Prefix Conversion</h3>
<p>This is the opposite of the previous function. With a given mask it should return the corresponding prefix. </p>
<p>Ex: <code>expect(IPV6.getPrefixForNetmask('ffff:ffff:ffff:ffff:ffff:fffc:0000:0000')).toBe(94);</code> </p>
<h4 id="explanation">Explanation:</h4>
<ol>
<li>Convert the mask to binary using <code>ip-address.js</code> function.</li>
<li>Count the number of 1's and return the count as result. </li>
</ol>
<pre><code class="lang-typescript">
<span class="hljs-comment">/**
   *
   * @param {string} netmask
   * @returns {number} prefix
   *
   * Returns exception if its an invalid mask
   */</span>
  <span class="hljs-keyword">static</span> getPrefixForNetmask(netmask: <span class="hljs-built_in">string</span>): <span class="hljs-built_in">number</span> {

      <span class="hljs-keyword">if</span> (IPV6.isValidMask(netmask)) {
        <span class="hljs-keyword">const</span> bits = <span class="hljs-keyword">new</span> Address6(netmask).getBitsBase2(<span class="hljs-number">0</span>, <span class="hljs-number">128</span>); <span class="hljs-comment">// Note : Address6 from ip-address.js </span>
        <span class="hljs-keyword">const</span> masked = bits.match(<span class="hljs-regexp">/1/g</span>);

        <span class="hljs-keyword">return</span> masked ? masked.length : <span class="hljs-number">0</span>;
      }

      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(<span class="hljs-string">'Invalid IPv6 Mask'</span>);
  }
</code></pre>
<h3 id="valid-mask">Valid Mask</h3>
<p>But before attempting to get the prefix out of the mask, we also need to check whether it is a valid mask, else it might return the wrong results. </p>
<p>Ideally, a valid mask can have a maximum of one flip bit in it and 
starts with 1 with trailing 0s or starts with 0 and no trailing 1s. </p>
<p>Example: 
1100 =&gt; valid // starts with 1 with trailing 0s
0000 =&gt; valid // with 0 and no trailing 1s
1010 =&gt; Invalid 
0001 =&gt; Invalid</p>
<h4 id="explanation">Explanation:</h4>
<ol>
<li>Convert the mask to binary string</li>
<li>Iterate through each bit to count the flips. </li>
<li>If the count is more than 1, then it's not a valid mask. </li>
</ol>
<pre><code class="lang-typescript">
<span class="hljs-keyword">static</span> isValidMask(mask: <span class="hljs-built_in">string</span>): <span class="hljs-built_in">boolean</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> bits = <span class="hljs-keyword">new</span> Address6(mask).getBitsBase2(<span class="hljs-number">0</span>, <span class="hljs-number">128</span>);
      <span class="hljs-keyword">let</span> flips = <span class="hljs-number">0</span>;
      <span class="hljs-keyword">let</span> oldBit = <span class="hljs-string">'1'</span>;

      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; bits.length; i++) {
        <span class="hljs-keyword">if</span> (oldBit !== bits.charAt(i)) {
          flips += <span class="hljs-number">1</span>;
          oldBit = bits.charAt(i);
        }

        <span class="hljs-keyword">if</span> (flips &gt; <span class="hljs-number">1</span>) {
          <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
        }
      }

      <span class="hljs-keyword">return</span> flips &lt;= <span class="hljs-number">1</span>;
    } <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }
  }
</code></pre>
<h3 id="bonus-random-ipv6-address-generator-used-for-testing">Bonus : Random IPv6 Address Generator ( used for testing)</h3>
<p>Sometimes I needed a random IPv6 address for testing. </p>
<h4 id="explanation">Explanation:</h4>
<ol>
<li>Create 7 * 4 chars chunks of individual sets with <code>Math.random()</code> and valid characters from IPv6 char set [0-9][a-f]. </li>
<li>Append them with the given prefix and join it with the colon(:).</li>
</ol>
<pre><code>
<span class="hljs-comment">/**
     *
     * @param ipPrefix defaults to 2000:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx
     * @returns a random IPv6 address with the given prefix
     */</span>
    <span class="hljs-keyword">static</span> generateRandomIPv6Address(ipPrefix = <span class="hljs-string">'2000'</span>): <span class="hljs-built_in">string</span> {
        <span class="hljs-keyword">const</span> items = <span class="hljs-string">'0123456789abcdef'</span>.split(<span class="hljs-string">''</span>);
        <span class="hljs-keyword">const</span> randomised = [];

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">28</span>; i++) {
            randomised[i] = items[<span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * items.length)];
        }

        <span class="hljs-keyword">const</span> ipBlock: <span class="hljs-built_in">string</span>[][] = [];

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; randomised.length; i++) {
            <span class="hljs-keyword">const</span> blockIndex = <span class="hljs-built_in">Math</span>.floor(i / <span class="hljs-number">4</span>);

            <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">Array</span>.isArray(ipBlock[blockIndex])) {
                ipBlock.push([]);
            }

            <span class="hljs-keyword">if</span> (!(ipBlock[blockIndex].length === <span class="hljs-number">0</span>) || randomised[i] !== <span class="hljs-string">'0'</span>) {
                ipBlock[blockIndex].push(randomised[i]);
            }
        }

        <span class="hljs-keyword">const</span> ip: <span class="hljs-built_in">string</span>[][] = [];

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; ipBlock.length; i++) {
            <span class="hljs-keyword">const</span> ipIndex = <span class="hljs-built_in">Math</span>.floor(i / <span class="hljs-number">8</span>);

            <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">Array</span>.isArray(ip[ipIndex])) {
                ip.push([]);
            }

            ip[ipIndex].push(ipBlock[i].join(<span class="hljs-string">''</span>));
        }

        <span class="hljs-keyword">let</span> suffixIp = <span class="hljs-string">''</span>;

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; ip.length; i++) {
            suffixIp += ip[i].join(<span class="hljs-string">':'</span>);
        }

        <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">${ipPrefix}</span>:<span class="hljs-subst">${suffixIp}</span>`</span>;
    }
</code></pre>]]></content:encoded></item></channel></rss>