# I built a type-safe SQL library for Bun — no ORM, no codegen, just SQL (using Claude Code)

I’ve been using Bun for a while and kept running into the same problem: every SQL library either requires Node.js internals, leans heavily on an ORM abstraction I don’t want, or generates types from a schema file at build time.

So I built squn — a lightweight, type-safe SQL query library that works natively with Bun’s built-in database clients.

The core idea

Every query goes through a tagged template literal called sql. Interpolated values always become bound parameters — they are never concatenated into the SQL string. SQL injection is structurally impossible by design.

import { createDb, PostgresAdapter, sql } from "@phonemyatt/squn";

const db = createDb(new PostgresAdapter({
  url: "postgresql://user:password@localhost:5432/mydb",
}));

interface User {
  id: number;
  name: string;
  age: number | null;
}

// Values become $1, $2 parameters — never string-concatenated
const users = await db.query<User>(sql`SELECT * FROM users WHERE age > ${18}`);

No schema file. No code generation step. No build-time magic. You write SQL, get back typed results.

Four databases, one API

squn supports all four databases you’re likely to use with Bun:

Database Driver
SQLite bun:sqlite (built-in)
PostgreSQL Bun’s native Postgres
MySQL Bun’s native MySQL
MSSQL mssql npm package

The same query code works across all four — only the adapter construction changes.

// Switch databases by swapping the adapter
const db = createDb(new SqliteAdapter({ filename: ":memory:" }));
const db = createDb(new PostgresAdapter({ url: process.env.PG_URL }));
const db = createDb(new MysqlAdapter({ url: process.env.MYSQL_URL }));
const db = createDb(new MssqlAdapter({ host: "localhost", ... }));

Query methods that match what you actually need

// All rows
const users = await db.query<User>(sql`SELECT * FROM users`);

// First row or null — no throw
const user = await db.queryFirst<User>(sql`SELECT * FROM users WHERE id = ${1}`);

// Exactly one row — throws if 0 or 2+ rows returned
const user = await db.querySingle<User>(sql`SELECT * FROM users WHERE id = ${1}`);

// Scalar — first column of first row
const count = await db.queryScalar<number>(sql`SELECT COUNT(*) FROM users`);

Composable SQL fragments

Fragments compose. Nested fragments merge inline and placeholders are renumbered automatically.

const minAge = 18;
const activeOnly = true;

const conditions = [
  sqlIf(minAge !== undefined, sql`age >= ${minAge}`),
  sqlIf(activeOnly, sql`active = ${true}`),
];

const where = sqlJoin(conditions, " AND ");
const q = sql`SELECT * FROM users WHERE ${where} ORDER BY name`;
// → SELECT * FROM users WHERE age >= $1 AND active = $2 ORDER BY name
// params → [18, true]

No string concatenation. No injection risk. Full composability.

Transactions that don’t leak

atomically wraps your callback in BEGIN/COMMIT and rolls back automatically on error:

await db.atomically(async (q) => {
  await q.execute(sql`UPDATE accounts SET balance = balance - ${100} WHERE id = ${from}`);
  await q.execute(sql`UPDATE accounts SET balance = balance + ${100} WHERE id = ${to}`);
  // if either throws, both updates are rolled back
});

Transaction also implements Symbol.asyncDispose — so await using gives you guaranteed cleanup:

await using tx = new Transaction(await adapter.beginTransaction());
await tx.execute(sql`UPDATE users SET active = ${false} WHERE id = ${42}`);
await tx.commit();
// if commit throws or you return early, rollback happens automatically

Batch inserts with a single prepared statement

await db.executeBatch(
  sql`INSERT INTO users (name, age) VALUES (@name, @age)`,
  [
    { name: "Alice", age: 30 },
    { name: "Bob",   age: 25 },
    { name: "Carol", age: 35 },
  ],
);

One prepared statement, all rows bound in a loop. Much faster than individual inserts.

Type inference from table definitions

Define your table schema once, get insert/select/update types inferred automatically:

import { col, defineTable, InferSelect, InferInsert } from "@phonemyatt/squn";

const Users = defineTable({
  id:   col("integer").primaryKey().notNull(),
  name: col("text").notNull(),
  age:  col("integer").nullable(),
});

type UserRow    = InferSelect<typeof Users>;  // { id: number; name: string; age: number | null }
type UserInsert = InferInsert<typeof Users>;  // { name: string; age?: number | null }

Multi-connection and read replicas

const db = createConnections({
  connections: {
    primary: new PostgresAdapter({ url: process.env.PRIMARY }),
    replica: new PostgresAdapter({ url: process.env.REPLICA }),
  },
  default: "primary",
});

// Route reads to replica
const users = await db.query<User>(sql`SELECT * FROM users`, { connection: "replica" });

// Scoped helper — no connection option needed per call
const replica = db.use("replica");

// Typed concurrent queries
const [users, roles] = await db.concurrent(
  db.query<User>(sql`SELECT * FROM users`),
  db.query<Role>(sql`SELECT * FROM roles`),
);

Try it

bun add @phonemyatt/squn

Feedback welcome — especially from anyone using it with MySQL or MSSQL in production.

Built with TypeScript 5.9 strict mode, zero any, and tested against real databases in Docker.

DynamoDB Contributor Insights Pricing: What You’re Actually Paying For

/$0.50 per rule per month. $0.03 per million events. Enabling Contributor Insights on a single DynamoDB table auto-creates four rules so your floor is $2.00/month before a single event is counted.

At 500 million events/month, that’s $17.00/month. At 5 billion, it’s $152.00/month.

Whether that’s justified depends on your traffic volume, whether you have hot key patterns, and what throttling is already costing you in wasted provisioned throughput.

What Is DynamoDB Contributor Insights?

Contributor Insights for DynamoDB is a CloudWatch monitoring feature that identifies the top partition keys driving read and write traffic on a table without any code-level instrumentation or custom application logging.

It analyzes request traffic in near real-time and publishes results as CloudWatch graphs. For tables with partition key skew, it answers the specific question a FinOps engineer needs: “Which partition key is causing throttling?”

The Two Pricing Components

Contributor Insights billing has exactly two line items on your CloudWatch invoice:

  • Rule fee: $0.50 per rule per month, flat regardless of traffic
  • Event fee: $0.03 per 1 million DynamoDB read/write events matched

How many rules does one table create?

Enabling on a single table auto-creates four rules:

  • Most-accessed partition keys on the base table
  • Most-accessed partition keys causing errors on the base table
  • Most-accessed partition keys on each GSI
  • Most-accessed partition keys causing errors on each GSI

Rule count formula: 2 + (2 × number of GSIs)

A table with no GSIs = $2.00/month in rule fees. A table with 3 GSIs = $4.00/month in rule fees, before any events.

What counts as an “event”?

Every read and write operation: GetItem, PutItem, UpdateItem, DeleteItem, BatchGetItem, BatchWriteItem (each item individually), Query and Scan (each item returned or examined), and transactional reads/writes. GSI operations are counted separately when rules are active on both.

Worked Cost Examples

10M events/month: $2.00 rule fee + $0.30 event fee = $2.30/month

500M events/month: $2.00 rule fee + $15.00 event fee = $17.00/month

2B events/month: $2.00 rule fee + $60.00 event fee = $62.00/month

2B events/month + 3 GSIs: $4.00 rule fee + $60.00 event fee = $64.00/month

For most workloads, the event fee is the variable that matters. The rule fee is fixed overhead.

Throttle-Only Mode

As of August 2025, AWS added a throttle-only mode. Rules only activate for events that result in throttling requests that exceed provisioned capacity or on-demand burst limits.

You still pay the $0.50/rule/month rule fee. But the event fee drops dramatically. On a well-provisioned table with a 0.1% throttle rate processing 2 billion events/month, you’d match roughly 2 million throttled events, an event cost of $0.06/month instead of $60.00/month.

Use throttle-only mode when:

  • You only need to diagnose which keys are causing throttling, not which are most popular
  • Your table is currently healthy and you want passive monitoring with near-zero cost
  • You have a high-volume table (1B+ events/month) where full mode runs $30+/month

Use full mode when:

  • You’re actively investigating a hot key pattern
  • You want continuous visibility into traffic distribution
  • You’re evaluating whether to restructure your partition key schema

If you’re deciding between 1-year and 3-year AWS commitment terms before locking in Reserved Capacity, the tradeoffs are covered in detail here How to Choose Between 1-Year and 3-Year AWS Commitments

The Real Cost Question: What Does Hot Key Blindness Cost?

Contributor Insights costs $2 to $62/month. The cost of not having it shows up in your provisioned capacity bill every month.

DynamoDB provisioned capacity is distributed across partitions. If your table is configured for 10,000 RCUs/second and 90% of reads go to three partition keys, the capacity on your other partitions sits idle while those three keys throttle.

Common patterns where this happens:

  • Low-cardinality partition keys (e.g., user_type, region, status)
  • Popular cached items that expire simultaneously, causing a read surge on one key
  • Time-based queries where all traffic clusters around “today” or “current”

A concrete scenario: a table provisioned at 5,000 WCUs/second costs approximately $2,847/month in write capacity (verify current rates at aws.amazon.com/dynamodb/pricing). If hot key throttling means 30% of that capacity is sitting idle on underutilized partitions, you’re paying ~$854/month for throughput you can’t use.

Contributor Insights at $17/month to identify which keys to redesign around: that’s a 50:1 return in the month you act on it.

*Hot Keys and Reserved Capacity Commitments
*

This is where hot key detection connects directly to cost optimization strategy.

DynamoDB Reserved Capacity delivers 30–40% savings on provisioned throughput. But those savings assume your capacity is being utilized effectively. A table with undetected hot key patterns has two compounding problems:

  • It’s over-provisioned because teams add capacity to compensate for throttling instead of fixing the partition key design
  • When you commit to Reserved Capacity on an over-provisioned table, you lock in the waste

The right sequence: identify hot keys via Contributor Insights → fix the partition key schema → right-size provisioned capacity → apply Reserved Capacity on that right-sized number. The result is a smaller commitment with higher utilization and 30–40% off the correct baseline.

Contributor Insights vs Standard CloudWatch Metrics

CloudWatch already provides DynamoDB metrics by default, at no charge. The key difference is specificity.

Standard CloudWatch metrics tell you “you have 50,000 throttled requests this hour.” Contributor Insights tells you “partition key user#12345 accounts for 48,000 of those throttled requests.”

For active troubleshooting or capacity planning, that distinction is the difference between guessing and acting.

When to Enable It

Enable Contributor Insights when:

  • Your table experiences recurring throttling that standard metrics can’t explain
  • You’re planning a table migration or partition key schema redesign
  • Your table processes 100M+ events/month and you have no visibility into traffic distribution
  • You’re considering a Reserved Capacity commitment and want to validate utilization first

Use throttle-only mode when:

  • You want passive monitoring with near-zero event costs
  • Your table is healthy but you want early warning if hot keys develop

Skip it entirely when:

  • You have very high cardinality partition keys (e.g., UUID-based) with no known skew
  • You’re on on-demand capacity mode with confirmed even traffic distribution
  • The table is non-production with no Reserved Capacity commitment

How to Enable It

Prerequisites: IAM permissions for cloudwatch:EnableInsightRules and dynamodb:DescribeTable. Understand the event cost for your traffic volume before enabling full mode.

  • Open the DynamoDB console and select your target table
  • Go to the Monitor tab
  • Click Manage Contributor Insights; select “All Requests” (full mode) or “Throttled Requests Only” (throttle-only mode)
  • Navigate to CloudWatch > Contributor Insights and confirm the rules are in “Enabled” state
  • Allow 15–30 minutes for data to appear; CloudWatch will show the top 10 partition keys by request volume

If rules show an “Error” state, the most common cause is insufficient IAM permissions. Verify the execution role includes cloudwatch:EnableInsightRules, cloudwatch:PutInsightRule, and dynamodb:DescribeTable.

How This Fits With Reserved Capacity

Identifying hot keys is a prerequisite to maximizing Reserved Capacity ROI. A table with hot key throttling shows a deceptive utilization signal: total consumed capacity may look high, but actual table-level efficiency is low because hot partitions throttle while cold partitions sit idle.

Usage.ai’s Flex Reserved Instances handle DynamoDB Reserved Capacity commitments with no upfront payment, no multi-year lock-in, and a cashback guarantee if utilization falls short see how it works at usage.ai

Usage.ai refreshes DynamoDB recommendations every 24 hours, vs. the 72+ hour refresh cycle on AWS native tools. At DynamoDB pricing, that 3-day gap in detecting newly-emerged hot key patterns can represent meaningful daily waste before any action is taken.

Have you run into hot key issues on DynamoDB? Did you catch it via Contributor Insights or some other way and what did the fix end up looking like?

Explore the full operational breakdown here → DynamoDB Contributor Insights Pricing: Monitoring Hot Keys at a Cost

Cloudflare Completes Its Agent Infrastructure Stack with Browser Run Rebuild and Six-Layer Platform

Cloudflare rebuilt Browser Run on its own Containers platform, delivering 4x higher concurrency and 50% faster response times. The upgrade completes a six-layer agent infrastructure stack: compute (Dynamic Workers + Sandboxes), orchestration (Dynamic Workflows), memory (Agent Memory), browsing (Browser Run), and commerce (Stripe Projects).

By Steef-Jan Wiggers

Oracle Linux 7 to 8 Upgrade Using Leapp: Architecture, Inhibitors, and Enterprise Troubleshooting

Introduction

Enterprise Linux operating systems require periodic upgrades to maintain security, supportability, compliance, and operational stability.

As organizations modernize infrastructure platforms, migrating from Oracle Linux 7 to Oracle Linux 8 becomes important because Oracle Linux 8 introduces:

✔ Modern package management
✔ Improved security
✔ Better kernel support
✔ Enhanced automation compatibility
✔ AppStream modular repositories
✔ Long-term enterprise support

However, major Linux upgrades are not simple package updates.

They involve:

✔ Repository transitions
✔ Package dependency changes
✔ Kernel migration
✔ Bootloader modifications
✔ Service compatibility validation
✔ Third-party package handling

Oracle Linux provides the Leapp upgrade utility to automate and orchestrate Oracle Linux 7 to Oracle Linux 8 migrations safely.

In this blog, we will perform a complete deep dive into:

✔ Configuring Leapp repositories
✔ Installing Leapp utility
✔ Preupgrade analysis
✔ Understanding inhibitors
✔ Answer file handling
✔ Repository migration
✔ Upgrade execution
✔ Upgrade boot workflow
✔ Enterprise troubleshooting
✔ Real-world operational challenges

Understanding Leapp

Leapp is an in-place upgrade utility used to migrate Oracle Linux 7 systems to Oracle Linux 8.

Why Major OS Upgrades Are Complex

Upgrading from Oracle Linux 7 to Oracle Linux 8 involves platform-level architectural changes.

These include:

✔ Kernel transitions
✔ Package replacement
✔ Repository mapping
✔ Driver compatibility
✔ Service migration
✔ Security policy updates
✔ Boot environment changes

Note : Operating system upgrades are not only package upgrades — they are full platform transitions.

Oracle Linux Upgrade Architecture

The Leapp upgrade process follows multiple operational stages.

Upgrade Workflow

Oracle Linux 7
│
▼
Repository Validation
│
▼
Leapp Installation
│
▼
Preupgrade Analysis
│
▼
Inhibitor Detection
│
▼
Answer File Validation
│
▼
Upgrade Initramfs Creation
│
▼
System Reboot
│
▼
Upgrade Environment Boot
│
▼
Package Migration
│
▼
Oracle Linux 8

Step 1: Verify Current Oracle Linux Version

Before starting the migration, validate the current OS version.

Command:

cat /etc/os-release

Example output:

NAME=”Oracle Linux Server”
VERSION=”7.x”

Step 2: Verify Current Repositories

Repository consistency is critical before performing upgrades.

Check repositories:

yum repolist

Why Repository Validation Matters

Broken or duplicate repositories may cause:

✔ Dependency failures
✔ Package mapping errors
✔ Upgrade inhibitors
✔ Incomplete migrations
✔ Boot failures

Add Leapp Repository Configuration

Navigate to repository directory:

cd /etc/yum.repos.d/

Create or validate Oracle Linux repositories.

Example:

[ol7_leapp]
name=Oracle Linux 7 Leapp Repository
baseurl=https://yum.oracle.com/repo/OracleLinux/OL7/leapp/x86_64/
enabled=1
gpgcheck=1

Refresh Repository Metadata
yum clean all
yum makecache

Step 4: Install Leapp Utility

Install required Leapp packages.

Command:

yum install -y leapp-upgrade leapp-data-oraclelinux

What Gets Installed?

Leapp installs:

✔ Upgrade actors
✔ Dependency analysis modules
✔ Migration logic
✔ Repository mapping data
✔ Upgrade workflows

Verify Leapp Installation

rpm -qa | grep leapp

Step 5: Understanding Leapp Preupgrade

Before performing the actual upgrade, Leapp performs extensive system analysis.

Command:
leapp preupgrade

What Happens During Preupgrade?

Leapp analyzes:

✔ Installed packages
✔ Drivers
✔ Repository configuration
✔ Bootloader state
✔ Kernel compatibility
✔ Unsupported packages
✔ Dependency conflicts
✔ Security policies

Operational Insight
The preupgrade phase prevents unsafe migrations before system modification begins.

Understanding Inhibitors

One of the most important Leapp concepts is inhibitors.

What Are Inhibitors?

Inhibitors are conditions that stop the upgrade from continuing safely.

If inhibitors exist, Leapp blocks the upgrade process.

Why Inhibitors Exist

Inhibitors protect systems from unsafe migration scenarios.

Examples:

✔ Unsupported repositories
✔ Duplicate repository entries
✔ Deprecated packages
✔ Missing answer files
✔ Third-party RPM conflicts
✔ Unsupported drivers
✔ Incorrect boot configuration

Real Repository Inhibitor Example

Example error:

Repository ol8_baseos_latest is listed more than once in the configuration.

Why This Happens

Possible causes:

✔ Duplicate .repo files
✔ Custom repositories
✔ Third-party repositories
✔ Incorrect migration preparation

Fixing Duplicate Repositories

Check repository directory:

ls -l /etc/yum.repos.d/

Review duplicate repository definitions:

grep -r “ol8_baseos_latest” /etc/yum.repos.d/

Remove duplicate entries carefully.

Understanding Leapp Answer Files

This is one of the most important upgrade concepts.

What Are Answer Files?

During upgrades, Leapp may require administrator confirmation for specific migration decisions.

Leapp stores these prompts inside answer files.

Location:

/var/log/leapp/answerfile

Why Answer Files Matter

Leapp blocks upgrades until required questions are answered.

Example:

Missing required answers in the answer file.

View Required Answers

Command:

leapp answer –section remove_pam_pkcs11_module_check.confirm=True

What Does This Do?

This command confirms specific upgrade actions required by Leapp.

Operational Insight

Answer files help administrators explicitly approve risky or environment-specific migration decisions.

Understanding Repository Migration

Oracle Linux 7 and Oracle Linux 8 use different repository structures.

Oracle Linux 7 Repositories

✔ ol7_latest
✔ ol7_UEKR6
✔ Optional repositories

Oracle Linux 8 Repositories

✔ BaseOS
✔ AppStream
✔ UEK repositories

Repository Mapping Workflow

OL7 Repositories
│
Repository Mapping
│
▼
OL8 BaseOS + AppStream

Understanding AppStream Repositories

Oracle Linux 8 introduces AppStream modular repositories.

Unlike Oracle Linux 7:

Packages are grouped into modules and streams.

Examples:

✔ Python streams
✔ NodeJS streams
✔ Database modules

This increases flexibility but also migration complexity.

Third-Party Repository Challenges

Enterprise systems commonly use third-party repositories.

Examples:

✔ EPEL
✔ Monitoring agents
✔ Security tools
✔ Vendor repositories
✔ Backup software

Example EPEL Problem

Example error:

No package epel-release available
Why This Happens

Possible causes:

✔ Repository incompatibility
✔ Incorrect release version
✔ Unsupported packages
✔ Missing metadata

Step 6: Execute Upgrade

Once inhibitors are resolved, begin the upgrade.

Command: leapp upgrade

What Happens Internally?

Leapp performs:

✔ Upgrade initramfs creation
✔ Bootloader modification
✔ Package migration
✔ Repository transition
✔ Kernel migration
✔ Service migration
Understanding Upgrade Initramfs

This is one of the most advanced upgrade concepts.

What is Upgrade Initramfs?

Leapp temporarily boots into a dedicated upgrade initramfs environment outside the running Oracle Linux 7 userspace.

This isolated environment safely performs package replacement operations.

Upgrade Boot Workflow

Normal OL7 Boot
│
▼
Upgrade Initramfs
│
▼
Package Migration
│
▼
OL8 Boot

Why Upgrade Initramfs Is Required

Leapp performs the upgrade outside the running Oracle Linux 7 userspace to avoid active package conflicts and dependency corruption during migration.

The temporary upgrade initramfs environment provides an isolated userspace where package replacement, repository switching, kernel migration, and dependency updates can occur safely without interfering with the currently running operating system.

This isolation significantly reduces the risk of package inconsistency and upgrade instability during major platform transitions.

Step 7: Reboot the System

After upgrade preparation:

reboot

The system boots into the temporary upgrade environment.

Migration occurs automatically.

Step 8: Verify Oracle Linux 8 Upgrade

After reboot completes:

Verify OS Version
cat /etc/os-release

Common Real-World Upgrade Problems

Many upgrade failures occur because of environment inconsistencies rather than Leapp itself.

Common Operational Problems

✔ Repository duplication
✔ Dependency conflicts
✔ Unsupported packages
✔ Missing drivers
✔ Bootloader issues
✔ EPEL incompatibility
✔ Service startup failures
✔ Network configuration mismatches
✔ SELinux conflicts

Upgrade Failure Recovery

If the upgrade process fails during migration, administrators should analyze Leapp reports, validate repositories, review inhibitors, and restore systems using backups or boot volume snapshots when necessary.

/var/log/leapp/leapp-report.txt

/var/log/leapp/leapp-upgrade.log

/var/log/leapp/leapp-preupgrade.log

Leapp logs provide detailed visibility into dependency analysis, migration stages, repository transitions, and package failures.

Rollback and Recovery Planning

Enterprise upgrades should always include rollback preparation.

Before upgrades:

✔ Create backups
✔ Snapshot boot volumes
✔ Validate rollback workflows
✔ Test upgrades in staging
✔ Document recovery procedures

Why Rollback Planning Matters

If upgrades fail unexpectedly:

Rollback capability reduces downtime and operational risk.
Enterprise Upgrade Best Practices

✔ Validate backups before upgrade
✔ Remove unsupported repositories
✔ Review Leapp reports carefully
✔ Resolve inhibitors completely
✔ Test upgrades in staging first
✔ Validate applications after migration
✔ Monitor services post-upgrade
✔ Maintain rollback procedures

Conclusion

Leapp provides a powerful and automated framework for migrating Oracle Linux 7 systems to Oracle Linux 8 through dependency analysis, repository validation, package migration, and upgrade orchestration.

Although the migration process is heavily automated, successful enterprise upgrades still require careful planning, repository consistency, inhibitor analysis, answer file validation, operational testing, and rollback preparation to ensure production stability.

Modern Linux upgrades are no longer simple package updates — they are enterprise platform modernization workflows requiring operational engineering discipline.

Responsive Web Design

Beginning:

Responsive web design is a web design or CSS style that makes a website look good on any screen size.

Note: The most popular responsive design technique is “Mobile First Design”.

To make the page optimized for mobile devices, first use this meta tag in the “head”
Сode:
<meta name="viewport" content="width=device-width, initial-scale=1" />

CSS Responsive Design can be achieved by using:

  • CSS units as percentages “%” (100%), “1fr” (for grid). “vw”.
  • “media query”.
  • CSS property: “max-width”, “min-width”.
  • Maybe something else.

A few of my own examples of what I use:

  1. If the width of the screen is 500 pixels or less, we do something, or if it is more than 500 pixels, we do something else.

@media(max-width: 500px){
p {
color: red;
}
}

@media(min-width: 500px){
p {
color: blue;
}
}

  1. This CSS code prevents images from overflowing and sets the automatic size for the picture:

img {
max-width:100%;
height: auto;
display: inline-block;
}

  1. My responsive content or page “wrapper” class. This wrapper in the example makes the content no wider than 560px and a width of 100% makes it adaptive to any screen:

.wrapper {
width: 100%;
max-width: 560px;
margin: 0 auto;
}

  1. Hide 1 and show 2 something depending on the screen size or dynamic style change. Hide the desktop version and show the mobile version of something instead. (A drop down menu or something else):

@media(width >= 500px){
p { color: red; }
}

@media(width <= 500px){
p { color: green; }
}

or

@media(width >= 500px){
.mobileVersion { display: none; }
.desktopVersion { display: block; color: red }
}

@media(width <= 500px){
.mobileVersion { display: block; color: green; }
.desktopVersion { display: none; }
}

  1. Personal experience and advice or technique:

I write CSS styles simultaneously for any screen, my method is the method of overriding the style above with the style below (hierarchy in CSS), for example, there is a regular style for large screens:

.testClass { color: red; }

Then under it I add the same CSS class and properties from the style above with values for other screens using CSS Media Queries:

.testClass { color: red; }

@media (max-width: 500px) {
.testClass { color: orange; }
}

@media (max-width: 300px) {
.testClass { color: blue; }
}

@media (max-width: 100px) {
.testClass { color: yellow; }
}

so I override the style above and create a new style with overriding method for screens that are not larger than 500 pixels, then for 200px with overwriting , then for 100px etc

  1. Note: Testing and debugging:
  • Resize the browser window to see the style changes.
  • Launch the developer tool in the browser through the menu and “Inspect” page and change the size of the window there.
  • In developer tools, you can choose mobile phone simulation: “Responsive Design Mode” (phone icon).
  • Of course, a test on a physical device.

Source and Learning:

Doc:

Presentation: AI Native Engineering

Ian Thomas shares a case study on embracing AI-native engineering within Meta’s Reality Labs. He explains the “Assess and Grow” framework, a maturity model designed to move teams from manual toil to AI-integrated innovation. He discusses real-world wins – including hitting 90% code coverage in record time – while addressing senior concerns like “code slop,” review fatigue, and maintaining quality.

By Ian Thomas

How We Picked Between PayPal and Crypto for a $500K/Year Digital Product Launch

The Problem We Were Actually Solving

In 2024 we launched a $99 CLI toolkit for engineers who automate cloud security posture. By month six we were processing $1,200 a day in PayPal fees and $300 a day in frozen funds awaiting manual review. Our chargeback rate was 0.4 %, well below the industry threshold, yet PayPal still hit us with a $2,500 rolling reserve. We had already moved our hosting and DNS to alternative providers to avoid central gatekeepers; payments were the last choke point.

What We Tried First (And Why It Failed)

We started with PayPal Payouts to pay affiliate commissions. The API latency was 4 seconds per payout, and every Sunday at 03:17 UTC the endpoint returned EMAIL_NOT_VERIFIED for 30 % of our 200 affiliates. Their support told us to log a ticket and wait three days; we missed our weekly payout window and lost three top affiliates.

Then we tried Stripe. Same problem: Stripe Radar flagged every European sale because the cardholder name contained an umlaut. Our false-positive rate reached 11 %, and Stripes phone support only works on weekdays. We were tired of begging for human escalation tickets.

The Architecture Decision

In April 2025 we built a dual-rail system: PayPal for US credit cards (still had to keep it for 60 % of our buyers) and Monero for the rest. Moneros 2.5-minute block time and 0.0004 XMR fee looked perfect on paper, but our first production batch revealed a flaw: users had to manually paste a payment URI into their wallet, which dropped conversion by 37 %.

We switched to a zero-knowledge checkout: the user clicks Purchase, the backend generates a session-specific Monero address with a 15-minute expiry, and the frontend polls our Node.js websocket endpoint for confirmation. The polling interval is 10 seconds, which gives us 90 % certainty within 2 minutes and still keeps the load under 120 requests per order.

The tradeoff was that we had to implement our own rate-limited memo decoder to prevent address-reuse spam. We chose the monero-cpp library over the Go wrapper because the C++ library exposed the transaction key scanning functions we needed; the Go version hid them behind a high-level struct that forced us to parse hex strings manually.

What The Numbers Said After

After six weeks on the dual system:

  • PayPal chargeback rate stayed at 0.4 %, but frozen funds dropped to $300 total (a 91 % reduction).
  • Monero orders grew from 12 % to 34 % of revenue.
  • Our biggest affiliate, who was based in Venezuela, started earning commissions again after PayPal banned him; he now refers 8 % of our traffic.
  • infra cost per $100 processed: PayPal $4.70, Monero $0.42.

The most painful lesson was chargeback handling. With PayPal we could dispute and win 78 % of cases automatically. With Monero there is no dispute layer; we simply mark a wallet as fraudulent and refund later. To compensate, we introduced a 30-second interactive CAPTCHA before checkout and reduced fraudulent orders from 1.8 % to 0.3 %.

What I Would Do Differently

I would not have waited until we hit $500K annual revenue to diversify. The moment PayPal placed our account under review for a single $2 transaction, we should have immediately spun up the zero-knowledge Monero flow instead of treating it as an experimental branch.

I would also have standardised on a single crypto library from day one. We wasted two engineer-weeks rewriting memo parsers after the Go wrapper changed its ABI between patch releases. Settling on monero-cpp and pinning the SHA to a4b1c3d would have saved us that pain.

Finally, I would have opened a small USD Coin (USDC) stream in parallel for US buyers who refuse Monero. It adds a third payment button but keeps our PayPal dependency to only 45 % of revenue, which is low enough to survive another arbitrary freeze.

Post-Mortem: Why My Blog Cover Images Silently Failed to Restore from S3

Six blog posts on my portfolio had broken cover images for longer than I’d like to admit. The images were in S3. The management command to restore them had been written and run. And yet — nothing. Blank covers, every time.

Here’s the full breakdown of what went wrong, why, and how it got fixed.

The Setup

My portfolio backend is a Cookiecutter Django project running on a DigitalOcean droplet with Docker Compose. Blog post cover images are stored on AWS S3. The cover field on the BlogPost model is an ImageField that stores a relative path like blog_posts/deploy.webp — Django’s S3 storage backend handles prepending the media/ prefix and building the full URL.

When I migrated away from using Hashnode as a headless CMS and imported all posts into my own Django backend, the cover images came along as CUID-based filenames (e.g. blog_posts/cmoxrumae00ms2em7bje5at07.png). Those CUIDs don’t exist in S3 — the actual files were uploaded separately with descriptive names like deploy.webp, manual.webp, sortinghashnode.webp.

To fix this, Gemini wrote restore_covers.py, a management command with two matching strategies:

  1. Exact CUID match — looks for blog_posts/{cuid}.webp etc. in S3
  2. Fuzzy slug match — tokenizes the post slug and looks for S3 filenames with overlapping keywords

The command was run. Six posts still had broken covers.

Root Cause 1: The Fuzzy Matcher Couldn’t Tokenize Concatenated Filenames

The S3 filenames are lowercase concatenated words: sortinghashnode.webp, trackingpage.webp, postmortem.webp. The fuzzy matcher works by calling get_keywords() on both the post slug and each S3 filename, then computing the set intersection.

Here’s the problem. get_keywords() uses re.findall(r"[a-zA-Z0-9]+", text) to tokenize. Applied to a filename:

get_keywords("sortinghashnode.webp")
# → {"sortinghashnode"}   ← one token

And for the post slug:

get_keywords("sorting-hashnode-series-posts-how-to-display-the-latest-post-first")
# → {"sorting", "hashnode", "series", "posts", "display", "latest", "first"}

The intersection of {"sortinghashnode"} and {"sorting", "hashnode", ...} is empty. Score = 0. No match.

The same failure applied to every concatenated filename in the bucket. trackingpage.webp couldn’t match a slug containing tracking and page. postmortem.webp couldn’t match a slug containing mortem (since post is a stop word). None of them scored above 0.

The fix was to replace the plain set intersection with substring containment, enforcing a minimum token length of 4 characters to prevent false positives from short words:

min_token_len = 4
overlap = set()
for pk in post_keywords:
    for fk in file_keywords:
        if pk == fk or (
            len(pk) >= min_token_len
            and len(fk) >= min_token_len
            and (pk in fk or fk in pk)
        ):
            overlap.add(pk)

Now "sorting" in "sortinghashnode" → True, score += 1. "hashnode" in "sortinghashnode" → True, score += 1. The correct file gets matched.

Root Cause 2: No AWS Credentials in the Local Environment

The second reason the command silently failed: it was run via docker compose -f docker-compose.local.yml, which loads .envs/.local/.django. That file has no AWS credentials.

When storage.listdir("blog_posts") is called with no S3 credentials, it either errors out silently (caught by a bare except Exception) or returns an empty list because the local filesystem storage backend is active instead of S3. The command’s output showed:

Could not list storage directory directly: ...
Fuzzy matching won't be available.

But the overall command still exited 0 with a summary that made it look like it ran fine. With zero files in storage_files, the fuzzy loop had nothing to iterate over — so every post hit the “no existing file found in storage” branch.

The management command is designed to run in the production container, where .envs/.production/.django already has the correct AWS credentials wired up.

Root Cause 3: One Image Was Never Uploaded to S3

Even with both fixes above, the post “How I Fixed the Hashnode GraphQL API Stale Cache Bug (Stellate CDN)” would still have a broken cover — because no matching file exists in S3 at all. The DB had blog_posts/cmlyqj0cc006627lvguola3gg.png and no file with a descriptive name was ever uploaded for it.

This one requires a manual upload via Django admin.

The Fix

For the five posts with known S3 matches, I wrote a Django data migration that directly sets the correct cover paths:

COVER_FIXES = {
    "how-to-manually-backup-wordpress-sites-via-ssh": "blog_posts/manual.webp",
    "deploying-cookiecutter-django-on-a-digitalocean-droplet-ubuntu-24-04-lts": "blog_posts/deploy.webp",
    "post-mortem-the-march-2026-axios-supply-chain-attack": "blog_posts/postmortem.webp",
    "sorting-hashnode-series-posts-how-to-display-the-latest-post-first": "blog_posts/sortinghashnode.webp",
    "tracking-page-views-in-a-react-spa-with-google-analytics-4": "blog_posts/trackingpage.webp",
}

def fix_covers(apps, schema_editor):
    BlogPost = apps.get_model("blogs", "BlogPost")
    for slug, cover_path in COVER_FIXES.items():
        BlogPost.objects.filter(slug=slug).update(cover=cover_path)

This runs automatically on python manage.py migrate during the next production deploy — no manual SSH step needed.

For the fuzzy matcher, the substring containment fix was patched into restore_covers.py so future runs work correctly for any similarly named files.

For the sixth post, a manual image upload to Django admin is the remaining action item.

What I’d Do Differently

The real issue is that the restore command’s failure mode was too quiet. It logged “fuzzy matching won’t be available” but still printed a clean summary with zeroes in the “could not restore” column for cases where the file list was empty. That made it look successful.

A better design: if storage.listdir() fails entirely, the command should exit early with a non-zero code rather than continuing with no files to match against. Silently succeeding at nothing is worse than loudly failing at something.

The slug-to-filename mismatch was also a predictable problem from the start. The files were uploaded manually with short descriptive names, but the DB records came from Hashnode with long CUIDs. A mapping file (even a simple JSON dict of slug → filename) would have made the restore command deterministic instead of relying on fuzzy heuristics.

Meet Chip: The Circuit.Rocks Design Academy Mascot

If you’ve spent any time on our store, our socials, or the workbench at a Circuit.Rocks workshop lately, you’ve probably spotted a small green CRT-headed robot peeking out from behind a breadboard. That’s Chip — the official mascot of Circuit.Rocks Design Academy.

Who is Chip?

Chip is a tiny vintage-computer-style robot with a glowing lightbulb antenna — built equal parts curiosity and 5V logic. He lives on a maker’s workbench surrounded by oscilloscopes, breadboards, soldering irons, and the occasional half-finished project. Chip’s job is to make electronics feel approachable: not intimidating, not gatekept, just a fun thing you can poke at, break, and learn from.

Whenever you see Chip on a guide, a tutorial reel, a worksheet, or a workshop poster — it’s a signal that this is content built specifically to help beginners (and tinkerers refreshing their fundamentals) actually understand what’s going on under the hood, not just copy-paste a wiring diagram.

Why a mascot?

Electronics has a reputation problem. Datasheets are dense. Forum answers assume you already know the lingo. The first time you smell a fried capacitor, you wonder if you’re cut out for this at all.

Chip exists to flip that script. The notebook on his desk says “Bug in the code?” not because debugging is shameful, but because every maker fights bugs — Chip just happens to keep a cheerful expression while doing it. The mascot is a small, deliberate reminder that learning to build things is supposed to be messy, weird, and fun.

Where you’ll see Chip

Chip is the face of our growing Design Academy library — short-form tutorial videos, downloadable beginner guides, project walk-throughs, and component primers. If you’re new to electronics and want a starting place that doesn’t assume two semesters of circuit theory, the Chip page is where to begin.

You’ll also catch him on our Circuitrocks social channels, popping up next to product launches, workshop announcements, and the occasional “this resistor exploded, here’s why” cautionary tale.

Say hi

Have an idea for a Chip episode? A topic you wish someone would just explain from scratch? Drop us a message — and if you spot Chip merch in the wild, send a photo. He’s photogenic and he knows it.

Visit circuit.rocks/pages/chip to meet him properly.

Originally published on blog.circuit.rocks.

Robots are being built faster than your industry is watching. Here’s what you missed this week.

Figure AI went from 1 robot per day to 1 per hour — in 4 months. Japan Airlines signed a 3-year humanoid contract. $37 billion in VC landed this year alone. Physical AI moved fast this week. Here is everything your industry missed, and why it matters more than the headlines suggested.

Think about the last time an airline signed a 3-year contract on technology that was still experimental. You cannot, because they do not. Airlines operate in one of the most regulated, liability-conscious industries on the planet. When Japan Airlines committed to a humanoid robot program at Haneda Airport in May 2026, they were not running a pilot. They were making a procurement decision — the same way they procure ground equipment, check-in systems, or gate management software.

That is the moment Physical AI changed categories. Not from a product demo, not from a VC funding round, but from a boring, bureaucratic, multi-year service contract at an airport most of you have probably transited through.

And JAL is not alone. This week told a consistent story across four separate industries. Here is what happened — and what it means beyond the headlines.

The numbers first, so we have the same starting point

Metric Value
Figure AI production speed increase 24x — from 1 robot/day to 1 robot/hour in 4 months
VC invested in Physical AI in 2026 $37B — a new all-time annual record, already
Projected humanoid robot market by 2035 $200B (Barclays Research, May 2026)
Share of global humanoid deployments — China 85% in 2025

When the Supply Chain moves, the Market is real

The strongest signal I track is not what robot companies say — it is what component suppliers do. This week, Khgears International, a Taiwanese manufacturer of precision gearboxes for industrial robots, announced a full pivot into humanoid-specific components: joints, drive mechanisms, actuator assemblies. They are seeking a strategic alliance with a Japanese Tier 1 automotive supplier to do it.

Khgears does not make this move on speculation. Gearbox manufacturers pivot when they have seen enough confirmed purchase orders to justify retooling their factory. When the supply chain moves, it means the demand is real, not projected.

The same signal comes from production lines. Figure AI went from producing one humanoid robot per day in January 2026 to one per hour by May — a 24x acceleration in four months. Their BotQ factory has already delivered 350+ units. For context: that is not a startup proving a concept. That is a manufacturer ramping toward industrial scale.

Why this matters: Component suppliers and production lines are lagging indicators — they follow confirmed demand. When they move at the same time, the market inflection has already happened. You are reading about the aftermath, not the prediction.

Robots are getting smarter faster than the hardware can keep up

Here is something that gets lost in the factory-and-funding coverage: the AI inside these robots is improving on a completely separate, faster curve. XPeng rolled the first mass-produced L4 robotaxi off its Guangzhou line on May 18 — and the AI model powering that car is the exact same model running their Iron humanoid robot. One model trained once, deployed in two completely different physical systems.

This is a bigger deal than it looks. Training a frontier vision-language-action model costs tens of millions of dollars. If that cost is shared between an autonomous vehicle fleet and a humanoid workforce, the unit economics of Physical AI become dramatically better than analysts currently model. NVIDIA made the same bet in a different direction — their Isaac GR00T models are now open source, meaning any robotics company can build on a foundation instead of starting from scratch.

DARPA is already asking what comes after this architecture entirely. Their May 2026 research call imagines robots where the material itself computes — no central processor, no cloud, no latency. That is a 10-year horizon, but DARPA’s early bets have a habit of becoming everyone’s reality.

Why this matters: Shared AI models across platforms mean the cost of building capable robots is falling faster than the hardware suggests. The gap between “what robots can do in a lab” and “what they cost to deploy at scale” is closing from both ends simultaneously.

What this actually means if you are not an Investor

A direct note to everyone who works in logistics, manufacturing, aviation, or any field with structured physical tasks: the companies in this article are not running experiments in your industry. They are operating under multi-year service contracts. The question is no longer whether robots will enter your workplace. It is which tasks they take first, and how fast.

Barclays Research framed the macro picture in their May 2026 report: the humanoid robot market could reach $200 billion by 2035, and for China specifically, robots may offset up to 60% of the demographic workforce decline projected over the next decade. That last number is not a technology story — it is a labor economics story, and it will play out in every aging economy, not just China’s.

The honest answer to “will robots take my job?” is still nuanced. Agility Robotics‘ agreement with Toyota covers logistics tasks in a manufacturing plant — moving parts, not assembling them. The Vodafone pilot in Duisburg had robots detecting misplaced products and unsafe pallet stacking, not replacing warehouse managers. The pattern so far is robots handling the physically repetitive and physically risky parts of jobs humans already find exhausting. But the category is expanding, and the speed of expansion is the variable to watch.

Why this matters: The Barclays report title is “Robots roll out, economies rewire.” That word — rewire — is the honest one. Not replace, not eliminate. Rewire. The people who will navigate this best are the ones who start paying attention now, not when the robot is already at the next workstation.

What to watch next

  • Figure AI’s BotQ throughput in Q3 — sustaining 1 robot/hour would make them the first humanoid manufacturer at genuine industrial scale by year-end.
  • XPeng Iron deployment update — the first real test of whether one AI model can actually run both a robotaxi fleet and a humanoid workforce in production.
  • Physical Intelligence’s $1B raise — if it closes at the reported $11B valuation, it resets comparables for the entire sector and triggers a new wave of raises.
  • Khgears’ Tier 1 alliance — whoever they partner with signals which Japanese industrial giant is moving seriously into humanoid supply chains.
  • The 85% China concentration risk — one country accounting for 85% of global deployments is a geopolitical variable that no analyst is pricing correctly yet.

FAQ

Q: What is Physical AI and why does 2026 matter?

A: Physical AI is artificial intelligence that operates in the real, physical world — humanoid robots, autonomous vehicles, robotic arms that reason in real time. 2026 matters because procurement replaced experimentation: Japan Airlines, Toyota, Amazon, and Vodafone are signing multi-year service contracts, not running pilots. Figure AI is producing one humanoid per hour. Barclays forecasts a $200 billion market by 2035. The phase shift from R&D to deployment happened this year.

Q: Will humanoid robots replace human workers?

A: The current deployment pattern is task replacement, not job replacement — robots are taking over physically repetitive, dangerous, or high-precision tasks within jobs that remain human-managed. Agility Robotics at Toyota handles parts logistics; humans still run the line. The Barclays framing is more accurate: economies will “rewire” rather than simply lose jobs. The speed of that rewiring, however, is accelerating significantly in 2026, and the category of tasks robots can handle is expanding rapidly.

Q: Which companies should I be watching in Physical AI right now?

A: Figure AI for production velocity and deployment scale. XPeng for the shared AI model strategy across robotaxi and humanoid. Physical Intelligence for foundation model development (their $1B raise at $11B valuation is a sector bellwether). NVIDIA as infrastructure — Isaac GR00T is becoming the Linux of robotics AI. And watch Khgears and other component suppliers: they tell you what the demand actually is, not what companies claim it will be.

Physical AI Digest is a weekly briefing produced by xBerry — a tech company based in Poland building tools at the intersection of AI and operations.