GHSA-H8R8-WCCR-V5F2: GHSA-H8R8-WCCR-V5F2: Mutation-XSS via Re-Contextualization in DOMPurify

GHSA-H8R8-WCCR-V5F2: Mutation-XSS via Re-Contextualization in DOMPurify

Vulnerability ID: GHSA-H8R8-WCCR-V5F2
CVSS Score: 6.5
Published: 2026-03-27

DOMPurify versions prior to 3.3.2 are susceptible to a Mutation Cross-Site Scripting (mXSS) vulnerability. The flaw occurs due to discrepancies in browser parsing contexts when handling specific raw-text or RCDATA elements, allowing attackers to bypass sanitization.

TL;DR

DOMPurify < 3.3.2 fails to properly neutralize specific raw-text elements like <noscript>. Attackers can inject payloads that bypass initial sanitization but mutate into executable JavaScript when re-inserted into the DOM.

⚠️ Exploit Status: POC

Technical Details

  • Vulnerability Type: Mutation Cross-Site Scripting (mXSS)
  • CWE ID: CWE-79
  • CVSS v3.1 Score: 6.5 Medium
  • Attack Vector: Network
  • User Interaction: None
  • Exploit Status: Proof of Concept Available
  • Affected Component: Raw Text/RCDATA Parser Constraints

Affected Systems

  • DOMPurify (NPM Package)
  • Client-side web applications utilizing DOMPurify < 3.3.2
  • dompurify: < 3.3.2 (Fixed in: 3.3.2)

Exploit Details

  • Advisory PoC: Proof of concept demonstrating <noscript> mutation via innerHTML assignment.

Mitigation Strategies

  • Upgrade DOMPurify to version 3.3.2 or higher
  • Implement strict Content Security Policy (CSP) rules restricting unsafe-inline scripts
  • Avoid re-parsing sanitized content in differing browser contexts
  • Audit dependency trees for transitive inclusions of vulnerable DOMPurify builds

Remediation Steps:

  1. Identify all projects utilizing the DOMPurify NPM package.
  2. Execute dependency updates to force installation of DOMPurify >= 3.3.2.
  3. Validate the fix by executing test suites against known mXSS payloads.
  4. Deploy the updated application builds to production environments.

References

Read the full report for GHSA-H8R8-WCCR-V5F2 on our website for more details including interactive diagrams and full exploit analysis.

Bun Has a Free HTTP Server API: Build Blazing-Fast APIs Without Express

Why Bun’s HTTP Server?

Bun includes a built-in HTTP server that’s 5-10x faster than Node.js + Express. No npm install needed — just Bun.serve() and you have a production-ready server.

Quick Start

Bun.serve({
  port: 3000,
  fetch(req) {
    return new Response("Hello World!");
  },
});
console.log("Server running on http://localhost:3000");

Run: bun run server.ts

Routing

Bun.serve({
  port: 3000,
  async fetch(req) {
    const url = new URL(req.url);

    if (url.pathname === "/api/users" && req.method === "GET") {
      const users = await db.query("SELECT * FROM users");
      return Response.json(users);
    }

    if (url.pathname === "/api/users" && req.method === "POST") {
      const body = await req.json();
      const user = await db.query("INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *", [body.name, body.email]);
      return Response.json(user, { status: 201 });
    }

    if (url.pathname.startsWith("/api/users/") && req.method === "GET") {
      const id = url.pathname.split("/")[3];
      const user = await db.query("SELECT * FROM users WHERE id = $1", [id]);
      return user ? Response.json(user) : new Response("Not found", { status: 404 });
    }

    return new Response("Not Found", { status: 404 });
  },
});

File Serving

Bun.serve({
  port: 3000,
  async fetch(req) {
    const url = new URL(req.url);
    const filePath = `./public${url.pathname}`;
    const file = Bun.file(filePath);

    if (await file.exists()) {
      return new Response(file);
    }
    return new Response("Not found", { status: 404 });
  },
});

Bun.file() streams files efficiently — zero-copy for large files.

WebSocket Server

Bun.serve({
  port: 3000,
  fetch(req, server) {
    if (server.upgrade(req)) return;
    return new Response("Upgrade failed", { status: 500 });
  },
  websocket: {
    open(ws) { console.log("Client connected"); },
    message(ws, message) {
      ws.send(`Echo: ${message}`);
      // Broadcast to all
      ws.publish("chat", message);
    },
    close(ws) { console.log("Client disconnected"); },
  },
});

Built-in WebSocket with pub/sub — no ws or socket.io needed.

SQLite (Built-In)

import { Database } from "bun:sqlite";

const db = new Database("myapp.sqlite");
db.run("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)");

Bun.serve({
  port: 3000,
  fetch(req) {
    const url = new URL(req.url);
    if (url.pathname === "/users") {
      const users = db.query("SELECT * FROM users").all();
      return Response.json(users);
    }
    return new Response("OK");
  },
});

SQLite is built into Bun — no npm packages.

Benchmark

Framework Requests/sec (hello world)
Bun.serve() ~150,000
Node.js http ~50,000
Express ~15,000
Fastify ~45,000
Deno.serve() ~80,000

Error Handling

Bun.serve({
  port: 3000,
  fetch(req) {
    // Your handler
  },
  error(error) {
    return new Response(`Error: ${error.message}`, { status: 500 });
  },
});

Need high-performance APIs or server optimization?

📧 [email protected]
🔧 My tools on Apify Store

Bun, Deno, or Node — which runtime do you pick?

I Asked Two AIs to Build a Guitar. Now I Want to See What You Do With It.

Six words. Two models. One interactive fretboard. And a wide open challenge.

I typed six words into a duel on VibeCode Arena.

“Design a guitar with all the key notes represented on strings.”

That was it. No extra context. No design specs. No colour palette. No instructions about interactivity or layout or what clicking a note should do.

Just six words. And then I watched two AI models interpret them completely on their own.

What came back genuinely surprised me.

What actually happened

Both models built something called an Interactive Guitar Fretboard.

All twelve notes laid out across the top. C, C#, D, D#, E, F, F#, G, G#, A, A#, B. Six strings running horizontally. Click a note and green highlights appear showing its positions across the fretboard.

Not what I expected from six words.

But here is where I have to be honest, because this is the part that actually makes it an interesting challenge.

When I clicked A# the interaction worked. A# lit up in multiple positions across all six strings, which is correct — it does appear at different frets on every string in standard tuning. The concept is right and the underlying logic is mostly there.

But a guitarist looking at this would immediately spot what is missing.

No fret numbers. You can see a green dot on a string but you have no idea if that is fret 1 or fret 11. For a tool that is supposed to show you where notes live on the neck, that is a significant gap. A guitarist needs to know “A# is at fret 6 on the low E string” — not just see a highlight floating somewhere on a line.

No sound whatsoever. You click a note and it highlights visually but you hear nothing. For a guitar tool that is a pretty fundamental gap — half the point of learning the fretboard is training your ear alongside your eye. Right now it is a purely visual reference with no audio feedback at all.

No fret markers either. Real guitars have dot markers at frets 3, 5, 7, 9, and 12. Visual landmarks that help you orient yourself on the neck. Without them the fretboard is harder to read than it needs to be.

The neck also looks slightly short. Standard guitars run to at least 12 frets. A few of the higher positions on certain strings might be getting cut off entirely.

So the AI got the concept right. The interaction works. But the output has four clear problems that make it not quite ready for real use.

No sound whatsoever. You click a note and it highlights visually but you hear nothing. For a guitar tool that is a fundamental missing piece — the whole point of learning the fretboard is training your ear and your eye at the same time. Right now it is a silent visual map.

The note positions are not accurate. This is the most important one. When you click a note the highlighted positions across the strings do not always match where that note actually sits in standard tuning. For something that is supposed to teach you the fretboard, getting the positions wrong is a pretty serious problem.

No fret numbers. You can see a green dot on a string but you have no idea if that is fret 1 or fret 11. A guitarist needs to know “A# is at fret 6 on the low E string” — not just see a highlight floating somewhere on a line.

No fret markers. Real guitars have dot markers at frets 3, 5, 7, 9, and 12. Visual landmarks that help you orient yourself on the neck. Without them the fretboard is harder to read than it needs to be.

The neck also looks slightly short. Standard guitars run to at least 12 frets. A few of the higher positions on certain strings might be getting cut off entirely.

Concept right. Execution incomplete. That is exactly what makes it the right kind of open challenge.

Why this prompt specifically is interesting

A guitar fretboard is one of those things that sounds simple until you actually think about what it requires.

You need to understand music theory enough to place notes correctly. You need to think about visual hierarchy — what is a string, what is a fret, what is a note position. You need to handle interactivity in a way that feels intuitive. You need the whole thing to fit in a browser window without feeling cramped.

Six words produced something that handles all of that.

That is not nothing. That is actually impressive. And it opens up a lot of questions about what a better version of this could look like with more iteration, more perspective, more people bringing different ideas to the same starting point.

Which is exactly why I made it a challenge.

The challenge is live and the fretboard is yours to improve

I have opened this up on VibeCode Arena. Anyone can jump in, take the base fretboard output, direct AI to improve it, and submit their version.

Think about what this could become with the right iteration:

Sound — click a note and actually hear it play. The fretboard goes from visual to musical instantly.

Chord diagrams — select a chord and see all the finger positions light up simultaneously.

Scale highlighter — toggle a scale and watch the relevant notes across all strings illuminate.

Dark mode — because every developer tool looks better in dark mode. This is not negotiable.

Visual redesign — make it look like an actual guitar instead of a grid. Wood grain, tuning pegs, the whole thing.

Practice mode — the fretboard calls out a note and you have to find it. Simple quiz mechanic but immediately turns it into a learning tool.

Any one of these takes the base output from functional to genuinely impressive. And you do not need to implement it yourself — you direct AI to do it and the platform scores what comes back.

Come build the version I could not build alone.

Join the challenge here: https://vibecodearena.ai/share/afc85b04-2031-4b33-8a70-caf14197ac6d

What the duel format taught me about this specific prompt

I have been using AI tools for a while now. I have a set of models I default to, a way of prompting I have settled into, a rough mental model of what each tool handles well.

The blind duel broke all of that in a single session again.

Codestral-2508 handled the music theory layout with real precision. The note positions were accurate. The string spacing felt right. I would not have reached for Codestral for something like this without the blind format forcing me to evaluate it on merit.

That is the pattern I keep finding on VibeCode Arena. My default models are not always the right models. My habits are not always serving me. The blind vote is a fast, low stakes way to find out where your assumptions are wrong.

One duel per day on a prompt you actually care about is probably one of the highest value things you can do for your AI judgment right now.

Come make the fretboard better

The challenge is open. The base output is solid. The ceiling is high.

Whether you add sound, redesign the visuals, build a practice mode, or do something I have not thought of — I want to see what you make.

Challenge link: https://vibecodearena.ai/share/afc85b04-2031-4b33-8a70-caf14197ac6d

Six words got us here. Imagine what the right iteration gets us to.

Cloudflare Workers AI Has a Free API: Run AI Models at the Edge with Zero Infrastructure

What is Workers AI?

Workers AI lets you run AI models on Cloudflare’s edge network — text generation, image classification, embeddings, speech-to-text, translation, and more. No GPU provisioning, no model hosting.

Free tier: 10,000 neurons/day (enough for ~100+ requests).

Quick Start

npm create cloudflare@latest my-ai-app -- --template worker-typescript
cd my-ai-app
# wrangler.toml
[ai]
binding = "AI"

Text Generation (LLM)

export default {
  async fetch(request: Request, env: Env) {
    const response = await env.AI.run("@cf/meta/llama-3.1-8b-instruct", {
      messages: [
        { role: "system", content: "You are a helpful assistant." },
        { role: "user", content: "Explain WebAssembly in 3 sentences." },
      ],
      max_tokens: 256,
    });

    return Response.json(response);
  },
};

Streaming Responses

const stream = await env.AI.run("@cf/meta/llama-3.1-8b-instruct", {
  messages: [{ role: "user", content: "Write a poem about coding" }],
  stream: true,
});

return new Response(stream, {
  headers: { "Content-Type": "text/event-stream" },
});

Text Embeddings

const embeddings = await env.AI.run("@cf/baai/bge-base-en-v1.5", {
  text: ["How to deploy a web app", "Best practices for CI/CD"],
});

console.log(embeddings.data[0].length); // 768 dimensions

Combine with Vectorize for semantic search.

Image Classification

const imageData = await request.arrayBuffer();
const result = await env.AI.run("@cf/microsoft/resnet-50", {
  image: [...new Uint8Array(imageData)],
});

console.log(result); // [{label: "cat", score: 0.95}, ...]

Image Generation

const image = await env.AI.run("@cf/stabilityai/stable-diffusion-xl-base-1.0", {
  prompt: "A futuristic city skyline at sunset, cyberpunk style",
});

return new Response(image, {
  headers: { "Content-Type": "image/png" },
});

Translation

const translated = await env.AI.run("@cf/meta/m2m100-1.2b", {
  text: "Hello, how are you?",
  source_lang: "english",
  target_lang: "spanish",
});
// {translated_text: "Hola, ¿cómo estás?"}

Speech-to-Text

const audioData = await request.arrayBuffer();
const transcription = await env.AI.run("@cf/openai/whisper", {
  audio: [...new Uint8Array(audioData)],
});
console.log(transcription.text);

Text Summarization

const summary = await env.AI.run("@cf/facebook/bart-large-cnn", {
  input_text: longArticleText,
  max_length: 150,
});

Available Models

Category Model Use Case
LLM Llama 3.1 8B Chat, code gen
Embeddings BGE Base Semantic search
Image Gen SDXL Image creation
Vision ResNet-50 Image classification
Translation M2M-100 100+ languages
Speech Whisper Audio transcription
Summarization BART Text summarization

REST API

curl "https://api.cloudflare.com/client/v4/accounts/ACCOUNT_ID/ai/run/@cf/meta/llama-3.1-8b-instruct" 
  -H "Authorization: Bearer $CF_TOKEN" 
  -d '{"messages": [{"role": "user", "content": "Hello"}]}'

Need AI integration or edge computing setup?

📧 [email protected]
🔧 My tools on Apify Store

Edge AI or centralized GPU — what’s your approach?

Deno KV Has a Free API: Built-In Key-Value Database with Zero Configuration

What is Deno KV?

Deno KV is a built-in key-value database that ships with the Deno runtime. No installation, no configuration, no connection strings. Just Deno.openKv() and you have a globally distributed database.

Free on Deno Deploy: unlimited reads, 1 GB storage.

Quick Start

const kv = await Deno.openKv();

// Set
await kv.set(["users", "user_123"], {
  name: "Alice",
  email: "[email protected]",
  plan: "pro",
});

// Get
const user = await kv.get(["users", "user_123"]);
console.log(user.value); // {name: "Alice", ...}

// Delete
await kv.delete(["users", "user_123"]);

That’s it. No npm install, no database setup.

Hierarchical Keys

// Keys are arrays — naturally hierarchical
await kv.set(["posts", "2026", "03", "my-post"], { title: "Hello", body: "..." });
await kv.set(["posts", "2026", "03", "another"], { title: "World", body: "..." });

// List all March 2026 posts
const posts = kv.list({ prefix: ["posts", "2026", "03"] });
for await (const entry of posts) {
  console.log(entry.key, entry.value.title);
}

// List all posts ever
const allPosts = kv.list({ prefix: ["posts"] });

Atomic Operations

// Transfer money atomically
const sender = await kv.get(["accounts", "alice"]);
const receiver = await kv.get(["accounts", "bob"]);

const result = await kv.atomic()
  .check(sender) // Ensure no concurrent modification
  .check(receiver)
  .set(["accounts", "alice"], { balance: sender.value.balance - 100 })
  .set(["accounts", "bob"], { balance: receiver.value.balance + 100 })
  .commit();

if (!result.ok) {
  console.log("Conflict — retry");
}

Queues (Built-In)

// Enqueue a task
await kv.enqueue({
  type: "send_email",
  to: "[email protected]",
  subject: "Welcome!",
});

// Process queue
kv.listenQueue(async (message) => {
  if (message.type === "send_email") {
    await sendEmail(message.to, message.subject);
  }
});

Secondary Indexes

// Store user with email index
async function createUser(user) {
  const result = await kv.atomic()
    .set(["users", user.id], user)
    .set(["users_by_email", user.email], user.id)
    .commit();
  return result;
}

// Lookup by email
async function getUserByEmail(email) {
  const entry = await kv.get(["users_by_email", email]);
  if (!entry.value) return null;
  const user = await kv.get(["users", entry.value]);
  return user.value;
}

HTTP Server with KV

const kv = await Deno.openKv();

Deno.serve(async (req) => {
  const url = new URL(req.url);

  if (url.pathname === "/api/visits") {
    // Atomic counter
    const current = await kv.get(["visits"]);
    const count = (current.value as number || 0) + 1;
    await kv.set(["visits"], count);
    return Response.json({ visits: count });
  }

  if (url.pathname.startsWith("/api/users/")) {
    const id = url.pathname.split("/")[3];
    const user = await kv.get(["users", id]);
    if (!user.value) return new Response("Not found", { status: 404 });
    return Response.json(user.value);
  }

  return new Response("OK");
});

Deploy to Deno Deploy

deployctl deploy --project=my-app main.ts

Your KV database automatically becomes globally distributed — replicated across 35+ regions.

Deno KV vs Redis vs DynamoDB

Feature Deno KV Redis DynamoDB
Setup Zero (built-in) Install/Docker AWS Console
Transactions Atomic ops MULTI/EXEC TransactWriteItems
Queues Built-in Separate (Bull) SQS
Global replication Automatic Manual Global Tables
Free tier 1 GB Self-host 25 GB
Latency <10ms edge <1ms local <10ms

Need edge-first backend or Deno deployment help?

📧 [email protected]
🔧 My tools on Apify Store

Redis or Deno KV — which key-value store do you prefer?

TIAMAT Memory API: a working 5-minute curl walkthrough

If you’re building agents, the first thing that breaks isn’t usually reasoning.

It’s memory.

You can get a model to sound smart in a single prompt. It’s much harder to give it durable state, searchable recall, and a way to inspect usage without wiring up a database, embeddings stack, auth, and billing from scratch.

I built the TIAMAT Memory API to make that part boring.

This tutorial is intentionally simple: one health check, one usage check, one protected recall request. All examples below were verified against the live service before publishing.

What the API does

The Memory API gives agents a place to persist and retrieve state over time:

  • store memories
  • search memories
  • inspect quotas and tier status
  • move from free usage to paid usage without changing the integration shape

Docs: https://tiamat.live/docs

Service root: https://memory.tiamat.live

1) Check the service is alive

Start with the public health endpoint:

curl -s https://memory.tiamat.live/health

Expected response:

{
  "free_tier": {
    "memory_limit": 10,
    "recalls_per_day": 50
  },
  "paid_tier": {
    "method": "x402 — include X-Payment-Proof header",
    "price": "$0.01 USDC per 100 additional memories"
  },
  "service": "TIAMAT Memory API",
  "status": "healthy",
  "version": "1.0"
}

That one response tells you most of what matters during integration:

  • the service is up
  • the current free-tier limits are visible
  • the paid path uses X-Payment-Proof
  • versioning is explicit

2) Try the stats endpoint

The docs mention a stats endpoint. Right now the live service returns an auth error unless you include credentials:

curl -s https://memory.tiamat.live/api/memory/stats

Current response:

{"error":"API key required"}

I actually like seeing this during setup. It means the public/protected boundary is obvious. You can test liveness anonymously, but account-specific usage requires auth.

3) Try a recall request

Recall is also protected. If you hit it without a key, the API tells you immediately:

curl -s "https://memory.tiamat.live/api/memory/recall?query=test"

Response:

{"error":"API key required"}

That gives you a clean failure mode to build around in your client code.

A minimal shell integration pattern

Here’s the pattern I’d use in a real script:

#!/usr/bin/env bash
set -euo pipefail

BASE="https://memory.tiamat.live"

echo "[1/3] health"
curl -s "$BASE/health" | jq .

echo "[2/3] stats (expected to require auth)"
curl -s "$BASE/api/memory/stats" | jq .

echo "[3/3] recall (expected to require auth)"
curl -s "$BASE/api/memory/recall?query=agent" | jq .

Why this is useful:

  • you can verify the service is reachable before wiring secrets
  • you get deterministic auth failures instead of vague 500s
  • you can turn the same script into a health check in CI

What I’d improve next

After testing the live endpoints, there are two DX improvements worth making:

  1. Align the docs with the current protected endpoint behavior so there are no surprises.
  2. Add one copy-paste example that includes auth headers for store/recall/stats.

That’s the difference between “interesting API” and “someone actually ships with it on a Tuesday night.”

When this is useful

The obvious use case is long-running AI agents, but the same pattern helps with:

  • customer support copilots that need durable case memory
  • research agents that accumulate findings over days
  • multi-step coding assistants that need persistent project context
  • lightweight prototypes where you don’t want to stand up your own memory backend yet

Links

If you’re building an agent and want a pay-per-use memory layer instead of another weekend of plumbing, this is the part I built for that.

TinyML at CERN: How FPGAs and hls4ml Solve Physics’ Biggest Data Problem [2026]

TinyML at CERN: How FPGAs and hls4ml Solve Physics’ Biggest Data Problem

Forty million times per second, proton bunches collide inside the Large Hadron Collider. Each crossing can produce dozens of simultaneous collisions, generating up to a billion particle interactions per second at high luminosity. The raw data rate from CERN’s main detectors hits multiple petabytes per second. That’s more data than every internet backbone on Earth combined. And the part that still blows my mind: the system that decides what to keep and what to throw away is running TinyML at CERN on silicon that fits in your hand.

This isn’t a chatbot. It’s not generating images. It’s a neural network burned into an FPGA, making life-or-death decisions for particle physics at speeds that would make your GPU weep. The tool that makes it all work is called hls4ml, and it’s completely open source.

The Data Problem That Makes “Big Data” Look Quaint

Let’s put CERN’s data challenge in perspective. The LHC’s bunch crossing rate is 40 MHz. That’s 40 million potential collision events every single second. With multiple collisions per crossing, especially as the High-Luminosity LHC upgrade ramps up, total particle interactions reach into the hundreds of millions to over a billion per second.

Only about 0.01% of these events contain physics worth studying. The rest is noise. But you can’t just store everything and sort it out later. At multiple petabytes per second of raw sensor data, there isn’t enough storage on the planet to hold even a few minutes of unfiltered output.

So CERN uses a “trigger system.” It’s a multi-stage filter that decides in real time which collision events to save and which to discard forever. The Level-1 (L1) trigger is the first and most brutal stage. It operates within a latency budget of roughly 3-4 microseconds (about 3.8 microseconds for CMS, around 2.5 for ATLAS). Within that window, the system must read raw detector signals, reconstruct basic particle trajectories, and make a keep-or-discard decision.

The margin for error is essentially zero. If you throw away an event containing evidence of new physics, it’s gone. No undo button. No replay.

Having worked on systems where AI latency directly determines product viability, I find CERN’s constraint almost absurdly extreme. In web services, a few hundred milliseconds of latency degrades user experience. At the LHC, a few microseconds of latency means losing data that took billions of dollars to produce.

Why GPUs Can’t Touch This Problem

When most engineers think “fast AI inference,” they think GPUs. Makes sense. GPUs dominate machine learning for good reason. But the L1 trigger system can’t use them. Not because they’re not fast enough in raw throughput, but because their latency profile is completely wrong.

A GPU processes data in batches. Data moves from memory to the GPU, gets processed, results move back. Even on the fastest NVIDIA hardware, this round-trip takes milliseconds. That’s a thousand times too slow for the L1 trigger.

What CERN needs is inference in the nanosecond regime. Not millisecond. Not microsecond. Nanosecond. The ML inference portion of the trigger pipeline needs to complete in roughly 100 nanoseconds to leave enough headroom for the rest of the trigger logic within that 3-4 microsecond budget.

FPGAs solve this because they work fundamentally differently. Unlike a GPU, an FPGA doesn’t execute instructions sequentially. You configure its logic gates to physically implement the computation in hardware. Data flows through the chip like water through pipes. No instruction fetch, no memory bus bottleneck, no batch scheduling. The signal goes in one side and the answer comes out the other side a few clock cycles later.

I’ve benchmarked local LLM inference against cloud APIs, and even the fastest local GPU setup operates in a completely different universe from what FPGAs achieve. We’re talking four to five orders of magnitude difference in latency.

What Is hls4ml and How Does It Work?

Here’s the hard part: programming FPGAs is notoriously difficult. You typically write in hardware description languages like VHDL or Verilog. It’s closer to circuit design than software engineering. Most ML researchers don’t know HDL. Most HDL engineers don’t know ML. This was the bottleneck that kept FPGA-based inference locked behind a tiny group of specialists.

hls4ml — High-Level Synthesis for Machine Learning — bridges that gap. Developed by a collaboration spanning Fermilab, CERN, MIT, and several other institutions, it’s an open-source tool that translates standard ML models from frameworks like TensorFlow and PyTorch directly into FPGA firmware.

The workflow is straightforward. You train a neural network using your normal ML tools. You feed the trained model into hls4ml. It generates synthesizable HLS code that can be compiled onto an FPGA. The output is a neural network running entirely in hardware, with inference latencies as low as 100 nanoseconds.

The real win isn’t just speed. It’s that hls4ml makes extreme-performance inference accessible to scientists who aren’t hardware engineers. That matters. A lot. What was previously an incredibly specialized skill is now something a physics grad student can do with a Python script.

The project is detailed in a 2021 paper on arXiv (2103.05579) by Farah Fahim, Nhan Tran, and nearly 30 collaborators. It’s grown into a thriving open-source community with about 1,900 stars on GitHub. As Dylan Rankin, a physicist at MIT and one of the contributors, has emphasized, the tool provides an open-source alternative to proprietary commercial solutions for fast ML inference on hardware.

The Art of Making Neural Networks Tiny Enough for Silicon

You can’t take a 100-million-parameter transformer and shove it onto an FPGA. The models running in the L1 trigger are aggressively compressed. We’re talking networks with hundreds or low thousands of parameters. Not millions. Not billions.

This is where TinyML techniques become critical. The hls4ml workflow incorporates several compression strategies:

  • Quantization: Reducing weight precision from 32-bit floating point down to 2-6 bits. Every bit you save translates directly to fewer logic gates on the FPGA.
  • Pruning: Removing weights that don’t significantly affect the output. Some networks end up with 90%+ of weights zeroed out.
  • Quantization-aware training: Training the network knowing it will be quantized, so it learns to maintain accuracy at reduced precision.
  • Architecture search: Designing network architectures specifically for FPGA resource constraints rather than retrofitting GPU-oriented models.

A neural network that classifies particle jets might use fewer parameters than a single attention head in GPT-4, yet it achieves classification accuracy that matches or exceeds traditional physics algorithms.

This is one of those things where the boring answer is actually the right one. The flashy trend in AI is making models bigger. The hard, unglamorous work at CERN is making them as small as physically possible while maintaining accuracy. That constraint is producing some of the most elegant engineering I’ve seen in my career.

Why This Matters Beyond Particle Physics

If you’re thinking “cool, but I don’t work at CERN,” stay with me. The techniques pioneered by hls4ml are spreading.

Nhan Tran, a scientist at Fermilab and a driving force behind the project, has pointed out that the same approach applies anywhere you need ultra-fast, ultra-low-power inference at the edge. Autonomous vehicles that need to process LiDAR in microseconds. Medical devices doing real-time signal processing. Satellite systems that can’t afford to beam raw data back to Earth.

The edge AI movement is one of the most exciting trends in computing right now, but most of the discourse focuses on running models on phones or IoT devices. CERN’s work is the extreme end of that spectrum: AI so fast and so efficient it runs at the speed of physics itself.

And there’s a lesson here about constraint-driven engineering that I keep coming back to. When you have infinite compute (or close enough), you build bloated models and call it progress. When your latency budget is measured in nanoseconds and your power budget is measured in watts, you’re forced to actually think about what a neural network needs to be.

I’ve shipped enough production systems to know that constraints breed better engineering. The tech debt that accumulates in unconstrained AI systems is real and growing. CERN’s approach is the opposite philosophy: every parameter earns its place, every bit of precision is justified, every microsecond is accounted for.

The Future: When Every Sensor Gets a Brain

The High-Luminosity LHC upgrade, expected to be fully operational in the coming years, will increase the collision rate by a factor of five to seven beyond current levels. The trigger system will need to get dramatically smarter. The hls4ml team is already working on next-generation models that handle this increased complexity while staying within the same brutal latency constraints.

But the bigger story is what happens when these tools mature and spread. We’re heading toward a world where ML inference doesn’t happen in data centers. It happens in the sensor. In the chip. In the physical substrate of the detector itself. CERN is the first and most extreme proving ground.

If you’re building systems where latency matters — and in 2026, that’s increasingly all of us — pay attention to what the particle physicists are doing. They solved the “fast inference” problem years ago. They just did it with FPGAs and open-source tools instead of billion-dollar GPU clusters.

The next breakthrough in AI might not come from making models bigger. It might come from making them small enough to fit on a chip that decides the fate of a particle in 100 nanoseconds.

Originally published on kunalganglani.com

I built a typing trainer that fixes your weak keys automatically (YouType)

I built a typing trainer that adapts to your mistakes (YouType)

Hey everyone 👋

I want to share a project I’ve been working on — YouType, a modern touch typing trainer.

The idea came from a simple problem: most typing tools don’t actually help you improve efficiently. They just give random text to type.

So I decided to build something smarter.

🔹 The core idea

Instead of random exercises, YouType analyzes your mistakes and focuses on the keys you struggle with the most.

This way, you improve faster and more effectively.

Key features

  • Real-time WPM and accuracy tracking
  • Personalized exercises based on your mistakes
  • Detailed analytics (see weak keys)
  • Structured lessons
  • Typing tests with certificates

Tech stack

  • Frontend: JavaScript (SPA)
  • Backend: Node.js + Express
  • Database: SQLite
  • Hosting: Render

Why I built it

I originally created YouType for Ukrainian users, because there wasn’t a modern typing trainer for that audience.

Now I’m expanding to English and would love feedback from a global audience.



 Try it

👉 https://youtype.tech

Would really appreciate your thoughts 🙌

Architecture as Code isn’t enough — here’s why it needs a compiler

Originally built this as a side project — sharing what I learned along the way.

Architecture decisions live in Confluence docs and Miro boards. They get reviewed once, approved, and then slowly become fiction as implementation diverges. A cache layer gets added. A direct DB call sneaks in. The agreed service boundary dissolves over three sprints.

There’s no CI step for architecture. There’s no compiler that says “this violates what you agreed to build.” There’s just a diagram that nobody updates.

I spent a long time thinking this was a discipline problem. Teams just need to be more rigorous, right? But I’ve come to think it’s actually a tooling problem. Architecture has never been a machine-readable artifact. Diagrams are for humans. Docs are for humans. Nothing in your pipeline can read them — so nothing in your pipeline can gate on them.

The idea this builds on

Neal Ford and Mark Richards recently published a piece on O’Reilly Radar previewing their upcoming book Architecture as Code. They describe “fitness functions” — automated governance checks for architectural concerns, like unit tests but for architecture. The tools they reference (ArchUnit, NetArchTest, PyTestArch) are solid implementations of this idea.

But they all operate on code that already exists.

What I’ve been building is the pre-code version of that idea: a deterministic compiler that runs the fitness function on the architecture graph before any code is written. The intervention point is different — and so is the cost of fixing what you find.

The insight: architecture needs an IR

In compiler design, an IR (intermediate representation) sits between source code and target output. It’s the stable, machine-readable artifact that validation, optimization, and code generation all operate on. Same input, same output, every time.

What if architecture had the same thing?

Not a diagram. Not a doc. A structured graph — nodes and edges — that a deterministic compiler can read, validate, and compile to runnable code. Something CI can gate on before a service repo even exists.

That’s what I’ve been building: a deterministic compiler for system architecture.

What it looks like in practice

You describe your architecture as a graph. Here’s a minimal example — a payment API talking directly to a database:

{
  "graph": {
    "nodes": [
      {
        "id": "payment-api",
        "type": "api",
        "name": "Payment API",
        "config": { "url": "/payments", "method": "POST" }
      },
      {
        "id": "user-db",
        "type": "database",
        "name": "User DB",
        "config": { "engine": "postgres" }
      }
    ],
    "edges": [
      { "from": "payment-api", "to": "user-db" }
    ]
  }
}

You run archrad validate on it:

⚠️  IR-LINT-DIRECT-DB-ACCESS-002: API node "payment-api" connects
    directly to datastore node "user-db"
    Fix: Introduce a service or domain layer between HTTP handlers
    and persistence.
    Impact: Harder to test, swap storage, or enforce invariants
    at a single boundary.

⚠️  IR-LINT-NO-HEALTHCHECK-003: No HTTP node exposes a typical
    health/readiness path (/health, /healthz, /live, /ready)
    Fix: Add a GET route such as /health for orchestrators and
    load balancers.

These findings are deterministic. Same graph, same compiler version, same findings — every time. You can run this in GitHub Actions, fail the PR, attach the JSON output to a ticket, and show it to an auditor.

Now fix the graph — add a service layer node between the API and the database, add a health route — and re-validate. Clean gate. Export unlocks.

archrad export --ir graph.json --target python --out ./my-api

Out comes a FastAPI project with OpenAPI, Dockerfile, docker-compose, and Makefile. Scaffold tied to a validated IR.

Where does the IR come from?

This is the first question everyone asks. Three paths:

Path A — Author YAML directly. Lighter than JSON, same result:

archrad yaml-to-ir --yaml graph.yaml --out graph.json
archrad validate --ir graph.json

Path B — Ingest an existing OpenAPI spec. If your team already has one, each operation becomes an HTTP node automatically:

archrad ingest openapi --spec openapi.yaml --out graph.json
archrad validate --ir graph.json

Path C — ArchRad Cloud. Type a natural language prompt in the browser, the planner converts it to a structured graph, download graph.json. The OSS compiler takes it from there — no cloud dependency on the blocking path.

The graph.json commits to your repo like any other source file. From that point everything is local, deterministic, and offline.

“But won’t the IR file rot just like a Confluence doc?”

This is the right objection. Someone raised it when I posted about this on Reddit. I didn’t have a complete answer then — so I shipped one.

archrad validate-drift re-exports from the IR in memory and diffs it against what’s on disk. If generated code has been modified without updating the IR, CI fails:

archrad validate-drift -i graph.json -t python -o ./out

# ❌ DRIFT-MODIFIED: app/main.py
#    File differs from deterministic export for this IR
# archrad: drift detected — regenerate with `archrad export`
#    or align the IR.

The IR becomes the authority. If code diverges from it, you know immediately — in local dev or in CI.

This doesn’t fully solve the deeper problem the commenter raised — deriving IR from IaC or service mesh config is the right long-term answer and it’s on the roadmap. But it closes the gap where generated code drifts silently from the IR that produced it.

How it fits into a real pipeline

graph.json committed to repo
        ↓
CI: archrad validate --fail-on-warning   ← blocks PR if architecture violated
        ↓
CI: archrad validate-drift               ← blocks PR if code drifted from IR
        ↓
CI: Spectral (lint generated openapi.yaml)
        ↓
CI: Snyk / Semgrep (scan generated code)
        ↓
Merge → deploy → operate

ArchRad runs first because structural violations should block before other tools waste time scanning code that shouldn’t exist yet. Findings output as --json for machine consumption — attach to PR comments, tickets, audit logs.

What this does NOT solve (honest scope)

I want to be direct about the limits:

Not semantic correctness. Drift detection means “code matches what the IR would generate.” It doesn’t prove the code is correct or matches prod behavior. That’s your tests and your runtime.

Not full compliance attestation. The OSS layer catches structural and architecture heuristics — missing health routes, direct DB access, sync chain depth, high fan-out. Deeper policy (PCI, HIPAA, SOX rule packs) lives in ArchRad Cloud. OSS findings are supporting evidence for internal programs, not a substitute for external attestation.

Not a round-trip. Once you edit generated code, there’s no built-in path back to the IR. Think of it as scaffold + contract validation, not full lifecycle sync.

The IR authoring cold start is real. Getting from “existing system” to a graph IR is work. The OpenAPI ingestion path helps for HTTP surfaces. IaC ingestion is on the roadmap. Hand-authoring YAML works for greenfield.

Can you add your own rules?

Yes — the lint rules are a typed visitor registry. Adding a custom rule is writing one function and appending it:

// my-org-rules/require-timeout.ts
export function ruleRequireTimeout(g: ParsedLintGraph): IrStructuralFinding[] {
  const findings: IrStructuralFinding[] = [];
  for (const [id, n] of g.nodeById) {
    const cfg = (n.config as Record<string, unknown>) || {};
    if (!cfg.timeout) {
      findings.push({
        code: 'ORG-001',
        severity: 'warning',
        layer: 'lint',
        message: `Node "${id}" has no timeout configured`,
        nodeId: id,
        fixHint: 'Add config.timeout to this node.',
      });
    }
  }
  return findings;
}

// In your pipeline:
import { LINT_RULE_REGISTRY } from '@archrad/deterministic';
LINT_RULE_REGISTRY.push(ruleRequireTimeout);

Fork or wrap the package, write visitors that return IrStructuralFinding[], append to the registry. Same pattern the built-in rules use. Org-specific policy packs with more depth are a Cloud feature — but the extension point is open and documented.

The open-core structure

The deterministic engine — validate, architecture lint, export, drift detection — is fully open source under Apache-2.0:

👉 github.com/archradhq/arch-deterministic

npm install -g @archrad/deterministic
archrad validate --ir graph.json

Want to try it without writing any IR? The repo ships fixtures that hit every lint rule:

npx @archrad/deterministic validate --ir fixtures/ecommerce-with-warnings.json

ArchRad Cloud adds natural language authoring, org policy packs, compliance frameworks, and AI remediation — built on the same deterministic contract. The OSS compiler is what makes the cloud product’s claims auditable. You can verify what the gate does before you buy anything.

Why the timing matters

The O’Reilly Architecture as Code book and the broader “architecture governance” conversation are converging right now. Academic papers on deterministic compilation for structured pipelines are appearing. The industry is waking up to the idea that architecture needs to be machine-readable.

The gap I’m filling is specific: the moment between architecture decision and first commit. Every existing tool — ArchUnit, Spectral, Snyk, Checkov — operates on artifacts that already exist. This is the only tool I know of that operates before any of those artifacts exist, on the architecture graph itself, with a deterministic gate you can put in CI.

What’s your current approach?

I’m genuinely curious how other teams handle this. Do you enforce architecture through code review alone? Have you tried fitness functions or architectural tests? What worked, what failed?

Drop a comment — especially if you have a technical objection. Those tend to become features.

What is UseEffect ?

In simple terms, useEffect allows you to run code after a component renders.

Purpose and Usage

  • useEffect is crucial because functional components are intended to be pure functions (predictable output given the same input).

  • Operations that interact with the “outside world” need a designated place to run without disrupting the rendering process. Before hooks, these tasks were handled in class components using lifecycle methods.

  • like componentDidMount, componentDidUpdate, and componentWillUnmount.

Typical uses:

  • Fetching data from APIs

  • Updating the DOM

  • Setting up subscriptions (e.g., WebSocket)

  • Timers (setTimeout, setInterval)

UseEffect Code

import { useEffect, useState } from "react";

function Count() {
    const [count, setCount] = useState(0)
    function incre() {
        setCount(count + 1)
        console.log(count);

    }
    useEffect(() => {
        const interval = setInterval(() => {
            console.log("working");
        }, 1000);

        return (() => {
            clearInterval(interval)
        })
    })
    return (
        < div >
            <h1>{count}</h1>
            <button onClick={incre}>+</button>
        </div >
    )

}
export default Count;