<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Agnel Nieves</title>
    <description>The latest articles on DEV Community by Agnel Nieves (@agnelnieves).</description>
    <link>https://dev.to/agnelnieves</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F168324%2Fb8ed8b67-086e-4f89-b626-cb7aa4d92b87.jpg</url>
      <title>DEV Community: Agnel Nieves</title>
      <link>https://dev.to/agnelnieves</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/agnelnieves"/>
    <language>en</language>
    <item>
      <title>He Built a Flight Simulator in Three Hours and Hit $1M a Year in 17 Days. Then It Went to Zero.</title>
      <dc:creator>Agnel Nieves</dc:creator>
      <pubDate>Wed, 24 Jun 2026 14:31:32 +0000</pubDate>
      <link>https://dev.to/promptway/he-built-a-flight-simulator-in-three-hours-and-hit-1m-a-year-in-17-days-then-it-went-to-zero-1b1l</link>
      <guid>https://dev.to/promptway/he-built-a-flight-simulator-in-three-hours-and-hit-1m-a-year-in-17-days-then-it-went-to-zero-1b1l</guid>
      <description>&lt;p&gt;In February 2025, Pieter Levels typed a sentence into Cursor. "Make a 3d flying game in browser with skyscrapers." Three hours later he had a working flight simulator. He had never made a game in his life.&lt;/p&gt;

&lt;p&gt;I went through his last couple hundred posts so you do not have to, and I want to show you both ends of this. The part where it printed money, and the part where it stopped.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;Levels is the indie hacker other indie hackers quote. Dutch, self-taught, builds everything solo in old PHP and runs it on a Hetzner box. His flagship is &lt;a href="https://photoai.com" rel="noopener noreferrer"&gt;Photo AI&lt;/a&gt;, which trains a model on your selfies and spits out studio-grade portraits.&lt;/p&gt;

&lt;p&gt;He posts his revenue. Constantly. That is the unusual thing about him. Most founders post a cropped Stripe screenshot once and disappear. Levels treats his Stripe dashboard like a public weather report.&lt;/p&gt;

&lt;h2&gt;
  
  
  The moment something worked, fast
&lt;/h2&gt;

&lt;p&gt;The flight sim, fly.pieter.com, was a lark. He shipped it, tagged it as built with Cursor in about three hours, and posted the prompt.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Today I thought what if I ask Cursor to build a flight simulator So I asked 'make a 3d flying game in browser with skyscrapers' And after many questions and comments from me I now have the official [ Pieter.com Flight Simulator ] in vanilla HTML and JS"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://x.com/levelsio/status/1893385114496766155" rel="noopener noreferrer"&gt;@levelsio on X&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It went off. Elon Musk reposted it. The game was free, so the money came from in-game ad slots, branded blimps, and a $29.99 F-16 upgrade. Thirteen days in he posted $67,000 in monthly recurring revenue. Then this.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"fly.pieter.com has now gone from $0 to $1 million ARR in just 17 days! Revenue update: $87,000 MRR (which is $1M ARR) My first project ever to go up this fast"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://x.com/levelsio/status/1899596115210891751" rel="noopener noreferrer"&gt;@levelsio on X&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Read that last line carefully, because he did. In the same post he wrote, "Whether the MRR is truly sustainable for a year we can only guess." He flagged the risk himself, in the victory tweet. The rest of the internet ignored that sentence and ran "1M ARR in 17 days" headlines for weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The moment something broke, quietly
&lt;/h2&gt;

&lt;p&gt;Here is the number the headlines never followed up on. As of his live X bio in mid-2026, the flight sim line reads "Pieter.com $0/m."&lt;/p&gt;

&lt;p&gt;Zero. The thing that hit $1M ARR in 17 days makes nothing now.&lt;/p&gt;

&lt;p&gt;That is not a scandal. It is the actual nature of the revenue. Sponsor slots and one-time jet purchases are not subscriptions that renew. Once the viral moment passed and the ad slots lapsed, the money left with the attention. Levels basically said as much at the peak. The crowd just preferred the clean version.&lt;/p&gt;

&lt;h2&gt;
  
  
  The product that actually pays the bills
&lt;/h2&gt;

&lt;p&gt;So ignore the game. The durable story is Photo AI, and it is more impressive precisely because it is boring.&lt;/p&gt;

&lt;p&gt;In September 2025 he posted a record month.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Photo AI just reached a new record of $150,000/mo. 2,573 active subscribers. 87% profit margin. 100% bootstrapped + $0 funding. Employees: 1 = just me on my laptop. Tech: PHP + jQuery + SQLite on a Hetzner VPS with Nginx and Ubuntu"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://x.com/levelsio/status/1970858876212756506" rel="noopener noreferrer"&gt;@levelsio on X&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One person. An 87% margin. A tech stack that working developers openly mock. When he posted that his code was "almost 14,000 lines of raw PHP mixed with inline HTML," the post pulled millions of views and a small civil war in the replies.&lt;/p&gt;

&lt;p&gt;His &lt;a href="https://x.com/levelsio" rel="noopener noreferrer"&gt;current X bio&lt;/a&gt; lists the portfolio: Photo AI at $100K/m, RemoteOK at $44K/m, Vibej.am at $39K/m, InteriorAI at $35K/m, and a few smaller lines, which puts him in the neighborhood of $245,000 a month across everything as of mid-2026. Notice Photo AI is at $100K there, down from that $150K record. These numbers move around. He is honest that records are usually one good day, not a new floor.&lt;/p&gt;

&lt;h2&gt;
  
  
  The verdict
&lt;/h2&gt;

&lt;p&gt;Here is the part the "one person, $150K a month" posts leave out, and Levels does not. He did not build an audience in 2025. He built it for a decade. His launches land because hundreds of thousands of people already trust his Stripe screenshots. After the Lex Fridman podcast he posted that sign-ups and revenue across almost all his sites doubled overnight, +93%, and in his $420K-record post he put Photo AI's bump specifically at 3x. That is an input you do not have.&lt;/p&gt;

&lt;p&gt;So the copyable lesson is not the three-hour build. AI genuinely compressed that, and that is real. The lesson is the boring middle. Charge from day one. He told &lt;a href="https://ppc.land/how-one-photo-ai-app-generates-132k-monthly-after-70-failed-startups/" rel="noopener noreferrer"&gt;PPC Land&lt;/a&gt;, "pay me money, pay $10, $20, $40. I would ask more than $10 per month." Ship the fix in two minutes, not two weeks. And know the difference between revenue that renews and revenue that visited once, took a selfie in an F-16, and never came back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://x.com/levelsio/status/1893385114496766155" rel="noopener noreferrer"&gt;X, flight sim build post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://x.com/levelsio/status/1899596115210891751" rel="noopener noreferrer"&gt;X, fly.pieter.com $1M ARR in 17 days&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://x.com/levelsio/status/1970858876212756506" rel="noopener noreferrer"&gt;X, Photo AI $150K record&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://x.com/levelsio/status/1837707857372106992" rel="noopener noreferrer"&gt;X, $420K one-day record post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://x.com/levelsio" rel="noopener noreferrer"&gt;Pieter Levels live profile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ppc.land/how-one-photo-ai-app-generates-132k-monthly-after-70-failed-startups/" rel="noopener noreferrer"&gt;PPC Land profile of Photo AI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>founders</category>
      <category>indiehackers</category>
      <category>photoai</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>Getting Your Writing Seen Beyond Your Own Site</title>
      <dc:creator>Agnel Nieves</dc:creator>
      <pubDate>Sat, 20 Jun 2026 14:37:18 +0000</pubDate>
      <link>https://dev.to/promptway/getting-your-writing-seen-beyond-your-own-site-2joi</link>
      <guid>https://dev.to/promptway/getting-your-writing-seen-beyond-your-own-site-2joi</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/blog/optimizing-your-site-for-ai-agents"&gt;Part one of this series&lt;/a&gt; covered your own site: llms.txt, structured data, machine-readable feeds, the eight layers that make your pages legible to humans and to the AI agents that increasingly read on their behalf. This piece is about everywhere else.&lt;/p&gt;

&lt;p&gt;The mental model is simple. Your site is the canonical home. Everywhere else is a spoke that points back to it. Spokes get you seen. The hub gets the credit. If you mix that up, you spend a year writing for someone else's audience and ranking for someone else's domain.&lt;/p&gt;

&lt;p&gt;I just finished wiring this distribution stack on Promptway, the site you are reading. Here is what I built, in the order I would build it again, and why each layer matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with full content feeds
&lt;/h2&gt;

&lt;p&gt;Most blogs ship a summary-only RSS feed by default. That is fine if RSS is just an archive ping for a few Feedly subscribers. It is a real problem the moment you want to syndicate.&lt;/p&gt;

&lt;p&gt;Every downstream channel that consumes your feed reads from it directly. &lt;a href="https://dev.to/devteam/revamped-rss-feed-imports-3j1e"&gt;dev.to's RSS importer&lt;/a&gt;, Flipboard magazines, Feedly previews, AI ingestion bots, every one of them pulls the article body out of the feed. If your feed has a summary, that is all they get. They cannot republish a full article from a 200 character teaser, so they either skip you or post a fragment that is useless to anyone who finds it.&lt;/p&gt;

&lt;p&gt;The fix is one feed change. In RSS, add &lt;code&gt;&amp;lt;content:encoded&amp;gt;&lt;/code&gt; with the full rendered HTML of the article wrapped in CDATA. In JSON Feed, populate &lt;code&gt;content_html&lt;/code&gt;. Keep the short &lt;code&gt;description&lt;/code&gt; or &lt;code&gt;summary&lt;/code&gt; field for clients that want a preview. While you are in there, add &lt;code&gt;&amp;lt;atom:link rel="self"&amp;gt;&lt;/code&gt; so the feed is self-aware, a &lt;code&gt;&amp;lt;link rel="hub"&amp;gt;&lt;/code&gt; pointing at a WebSub hub, and a &lt;code&gt;&amp;lt;media:content&amp;gt;&lt;/code&gt; block with your hero image so cards render with art.&lt;/p&gt;

&lt;p&gt;This is the foundation. Nothing else in this article works as well if the feed is summary-only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Push, do not wait to be pulled
&lt;/h2&gt;

&lt;p&gt;Crawlers used to be the only way new URLs got noticed. That is no longer true. There is a faster path for everything except Google, and it costs you one HTTP POST.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.indexnow.org/" rel="noopener noreferrer"&gt;IndexNow&lt;/a&gt;&lt;/strong&gt; is a small open protocol that lets you tell search engines about a new or updated URL the instant it ships. You generate a key, serve it from your domain as a static file, and POST your URLs to &lt;code&gt;https://api.indexnow.org/indexnow&lt;/code&gt;. One submission propagates to Bing, Yandex, Seznam, and Naver. Google is not a participant.&lt;/p&gt;

&lt;p&gt;Bing matters double. &lt;a href="https://yoast.com/chatgpt-search/" rel="noopener noreferrer"&gt;Seer Interactive's analysis&lt;/a&gt; found that roughly 87% of ChatGPT search citations overlap with Bing's top ten results. The fastest path into ChatGPT citations is being indexed by Bing the moment you publish, not three weeks later when its crawler eventually wanders in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.w3.org/TR/websub/" rel="noopener noreferrer"&gt;WebSub&lt;/a&gt;&lt;/strong&gt; does the same thing for feed readers. Add a &lt;code&gt;&amp;lt;link rel="hub"&amp;gt;&lt;/code&gt; to your RSS, then POST to the hub when you publish. Feedly, Inoreader, and similar readers subscribed to the hub get notified in seconds instead of on their normal polling interval. Google retired its hosted hub but &lt;a href="https://pubsubhubbub.appspot.com/" rel="noopener noreferrer"&gt;Superfeedr's free hub&lt;/a&gt; still works fine for low volume publishers.&lt;/p&gt;

&lt;p&gt;Both of these belong in your publish pipeline as one or two function calls. They are the highest-return two minutes of distribution work you will ever do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Syndicate with canonicals, always
&lt;/h2&gt;

&lt;p&gt;The standard objection to cross-posting is duplicate content. The fix is older than the problem. Every copy of your article on someone else's domain declares a &lt;code&gt;rel="canonical"&lt;/code&gt; link back to your version, plus a visible "originally published at" line for human readers. Google has &lt;a href="https://developers.google.com/search/blog/2023/02/google-search-and-ai-content" rel="noopener noreferrer"&gt;said for years&lt;/a&gt; that canonical syndication is fine, and dev.to, Medium, and Hashnode all support it natively.&lt;/p&gt;

&lt;p&gt;The targets worth the effort, in roughly this order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dev.to"&gt;dev.to&lt;/a&gt;&lt;/strong&gt; has an open API. Create an organization page, generate an API key, and &lt;code&gt;POST /api/articles&lt;/code&gt; with &lt;code&gt;canonical_url&lt;/code&gt; set to your version and &lt;code&gt;organization_id&lt;/code&gt; set to your org. Their RSS importer also works if you would rather skip the code path. Dev.to's audience reads new posts, and the engagement signals roll up to your domain via the canonical.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://hashnode.com" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;&lt;/strong&gt; has an RSS importer that respects canonicals. Their free GraphQL API reportedly went paid in May 2026, so prefer the RSS path unless you are already on a paid plan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://flipboard.com" rel="noopener noreferrer"&gt;Flipboard&lt;/a&gt;&lt;/strong&gt; has been courting indie publishers in 2026, but the self-serve RSS pipe into magazines is gone, the help article for it now returns a 404. What works today: create a magazine, flip your own articles into it by URL, mix in third-party links so it reads like a magazine instead of a billboard, and email &lt;a href="mailto:contentpartnerships@flipboard.com"&gt;contentpartnerships@flipboard.com&lt;/a&gt; with your feed URL if you want true source-level ingestion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://medium.com/p/import" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;&lt;/strong&gt; has an import flow per article that sets the canonical automatically. Their write API has been closed to new tokens since early 2025, so this is a manual step. Code blocks import poorly. Use it opportunistically for essay-style pieces.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rule across all of them: never let a syndicated copy live without a canonical pointing home. If a partner's CMS does not support &lt;code&gt;rel="canonical"&lt;/code&gt;, do not syndicate there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Social, with realistic expectations
&lt;/h2&gt;

&lt;p&gt;The honest truth about social in 2026 is that the only platform where automation is a clear win for indie publishers is Bluesky. The rest are either too expensive, too restricted, or too judgment-heavy to automate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://bsky.social" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;&lt;/strong&gt; lets you &lt;a href="https://bsky.social/about/blog/4-28-2023-domain-handle-tutorial" rel="noopener noreferrer"&gt;claim a domain handle for free&lt;/a&gt; by adding a DNS TXT record. Claim your domain as the handle, &lt;code&gt;@promptway.com&lt;/code&gt; in our case, for the same reason browsers show a padlock: the domain is the verification. The AT Protocol API is free and open, so a publish-time post is a few lines of code with an app password. EchoFeed at about $25 a year will do it for you from your RSS if you do not want to write the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt; is where the operator audience actually reads, especially for AEO and stack-of-tools pieces. Skip their API, which has a restricted approval process you will not pass as an indie. Post manually, or pipe through a tool like Typefully or Buffer. LinkedIn newsletters push to subscribers via email and notifications, which sidesteps the feed algorithm. Worth setting up once you are publishing weekly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;X&lt;/strong&gt; is a poor investment in 2026. The free API tier is closed to new developers, and in April 2026 &lt;a href="https://postproxy.dev/blog/x-api-pricing-2026" rel="noopener noreferrer"&gt;URL-containing posts were repriced to roughly $0.20 each&lt;/a&gt; on the paid tiers. Keep the account, post important pieces by hand, and put the automation budget anywhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Email is the channel you actually own
&lt;/h2&gt;

&lt;p&gt;Every platform above can change its rules, throttle your reach, or close its API tomorrow. Email cannot. It is the only channel where you own the list and the delivery mechanism.&lt;/p&gt;

&lt;p&gt;Two practical pieces. First, you need a transactional sender that can do broadcasts. I use &lt;a href="https://resend.com" rel="noopener noreferrer"&gt;Resend&lt;/a&gt; because the API is good, the React Email integration is good, and the dashboard is sane. Postmark and AWS SES work fine too. Second, on every publish, send a broadcast to your audience. This is the closest thing you have to a guaranteed reader.&lt;/p&gt;

&lt;p&gt;Do not skip &lt;a href="https://datatracker.ietf.org/doc/html/rfc8058" rel="noopener noreferrer"&gt;RFC 8058&lt;/a&gt; one-click unsubscribe. Gmail and Yahoo require it for any sender with a meaningful list, and inbox placement gets worse fast without it. Resend handles the headers if you use their broadcasts API; if you roll your own, add &lt;code&gt;List-Unsubscribe&lt;/code&gt; and &lt;code&gt;List-Unsubscribe-Post&lt;/code&gt; headers and honor the POST.&lt;/p&gt;

&lt;p&gt;A welcome email on signup is a nice touch and roughly doubles the chance a subscriber remembers who you are by the time the next broadcast arrives.&lt;/p&gt;

&lt;h2&gt;
  
  
  The channels that cannot be automated
&lt;/h2&gt;

&lt;p&gt;Some of the highest-value surfaces are also the ones where automation will get you banned, downranked, or quietly ignored. They are worth doing, but they are human work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reddit&lt;/strong&gt; is the biggest of these. &lt;a href="https://ziptie.dev/blog/why-reddit-dominates-chatgpt-perplexity-and-google-ai-overviews" rel="noopener noreferrer"&gt;Research from ZipTie&lt;/a&gt; found Reddit is the most cited domain in Perplexity, near the top in ChatGPT search, and a dominant source in Google's AI Overviews. The dynamic is straightforward: AI engines treat Reddit as a corpus of authentic human discussion, and they pull from it heavily. A single relevant comment with a link to your piece can drive more AI citations than a month of feed syndication.&lt;/p&gt;

&lt;p&gt;The catch is that Reddit kills self-promotion on sight. Two to four weeks of genuine participation in a subreddit before you ever post a link is the floor. Read the rules of each sub, the old sitewide 90/10 self-promo rule is retired and each subreddit governs itself now. Text posts that lead with the insight and link out as a citation tend to do better than link posts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hacker News&lt;/strong&gt; is the other one. It rewards founder-bylined essays with plain factual titles and punishes anything that smells like marketing or AI prose. If you have personas writing on your site, post only the pieces under your own name and from an account that comments on other people's submissions too. Do not submit your own work every week.&lt;/p&gt;

&lt;p&gt;These are slow channels. They are also where the citation flywheel actually spins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automate the mechanical, keep judgment human
&lt;/h2&gt;

&lt;p&gt;The split is the whole game. The mechanical work (indexing, syndication, broadcast, the Bluesky post) belongs in a script that runs on publish. The judgment work (which subreddit, when to post to HN, which LinkedIn comment to respond to) stays with you.&lt;/p&gt;

&lt;p&gt;My setup is a single &lt;code&gt;distribute.ts&lt;/code&gt; script that GitHub Actions runs after the deploy succeeds. It hits IndexNow, pings the WebSub hub, posts to dev.to via API, sends the Resend broadcast, and posts to Bluesky and Mastodon with the hero art, an author tag, and hashtags. It writes a row to a committed ledger file so re-runs are idempotent. Total code, about a thousand lines, and most of that is per-channel error handling so one flaky API never blocks the rest.&lt;/p&gt;

&lt;p&gt;The matching human side is a 15 minute per-article checklist: request indexing in Google Search Console, share-rail posts, one relevant subreddit if it genuinely fits, Medium import. Fifteen minutes is short enough that I actually do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure what is actually working
&lt;/h2&gt;

&lt;p&gt;The headline number to track is not pageviews. It is AI referrals.&lt;/p&gt;

&lt;p&gt;In GA4, build a custom channel group that captures the referrers worth knowing about: &lt;code&gt;chatgpt.com&lt;/code&gt;, &lt;code&gt;perplexity.ai&lt;/code&gt;, &lt;code&gt;claude.ai&lt;/code&gt;, &lt;code&gt;gemini.google.com&lt;/code&gt;. Industry data puts AI referrals around 1% of pageviews for most publishers, but they convert at multiples of organic search traffic. They are small, high-intent visits from people whose AI assistant has already vouched for you.&lt;/p&gt;

&lt;p&gt;The other dashboards: Google Search Console for indexed pages and impressions, Bing Webmaster Tools for the IndexNow side, Resend for audience growth per broadcast, dev.to organization analytics for the syndicated reach. Look weekly, not daily. None of this moves fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shape of it
&lt;/h2&gt;

&lt;p&gt;The whole stack, in one breath: ship full content feeds, fire IndexNow and WebSub on publish, syndicate with canonicals, post to Bluesky and LinkedIn, email your list, participate honestly on Reddit and Hacker News, and watch the AI referrer channel quietly grow.&lt;/p&gt;

&lt;p&gt;Most of it is one-time setup. The publish pipeline runs itself once it is wired. The human channels stay human. The site stays canonical.&lt;/p&gt;

&lt;p&gt;The point is not to be everywhere. It is to make sure that when a human or an AI engine wants to find your work, every spoke they touch sends them back to the same place: your domain, your byline, your archive.&lt;/p&gt;

&lt;p&gt;That is what gets your writing seen.&lt;/p&gt;

</description>
      <category>distribution</category>
      <category>syndication</category>
      <category>indexnow</category>
      <category>websub</category>
    </item>
    <item>
      <title>He Made $189,000 in Profit in a Single Month, Then Sold the Whole Thing to Wix for $80 Million. Six Months In.</title>
      <dc:creator>Agnel Nieves</dc:creator>
      <pubDate>Tue, 16 Jun 2026 15:33:40 +0000</pubDate>
      <link>https://dev.to/promptway/he-made-189000-in-profit-in-a-single-month-then-sold-the-whole-thing-to-wix-for-80-million-six-o30</link>
      <guid>https://dev.to/promptway/he-made-189000-in-profit-in-a-single-month-then-sold-the-whole-thing-to-wix-for-80-million-six-o30</guid>
      <description>&lt;p&gt;The lawyers finished the Base44 paperwork on a Thursday night. The signing was set for Friday morning. That same Friday morning, the war with Iran broke out. Maor Shlomo signed anyway.&lt;/p&gt;

&lt;p&gt;That detail is the whole story in miniature. A guy in Israel, between reserve duty call-ups, sells a company he started as a side project, and the missiles are a scheduling problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;Shlomo was not a kid with a dream and a laptop. He had already co-founded Explorium, a data analytics company that raised a total of $127 million, capped by a $75 million Series C led by Insight Partners, per &lt;a href="https://techcrunch.com/2025/06/18/6-month-old-solo-owned-vibe-coder-base44-sells-to-wix-for-80m-cash/" rel="noopener noreferrer"&gt;TechCrunch&lt;/a&gt;. He left, did reserve duty after October 7, and came back restless.&lt;/p&gt;

&lt;p&gt;Base44 started because his girlfriend needed a website for her art business and he was helping the Israeli Scouts with some software. He kept hitting the same wall. As he told &lt;a href="https://ceoinsider.io/interview/maor-shlomo" rel="noopener noreferrer"&gt;CEO Insider&lt;/a&gt;, "Both times I realized, 'LLMs should be able to build this.'"&lt;/p&gt;

&lt;p&gt;So he built the thing that builds the thing. Base44 turns a plain-English prompt into a working web app, database and login and all. No code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The moment something worked
&lt;/h2&gt;

&lt;p&gt;Here is where the numbers start, and they are silly.&lt;/p&gt;

&lt;p&gt;He told his girlfriend that if they hit $1.5M in annual recurring revenue by the end of 2025, they would buy a nice car. They hit it in four weeks. He said this out loud on &lt;a href="https://www.lennysnewsletter.com/p/the-base44-bootstrapped-startup-success-story-maor-shlomo" rel="noopener noreferrer"&gt;Lenny's Podcast&lt;/a&gt;, the same place he confirmed he hit $1 million ARR three weeks after launch and grew to more than 400,000 users without spending money on marketing.&lt;/p&gt;

&lt;p&gt;Ten thousand users showed up in the first three weeks. By the six-month mark he was reportedly at 250,000 users, per &lt;a href="https://techcrunch.com/2025/06/18/6-month-old-solo-owned-vibe-coder-base44-sells-to-wix-for-80m-cash/" rel="noopener noreferrer"&gt;TechCrunch&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then the profit number. In May 2025, Base44 made $189,000 in profit. Not revenue, profit. Shlomo posted it himself on LinkedIn, and the figure got passed around the founder world fast.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Base44 ended up making $189K &lt;em&gt;profit&lt;/em&gt; in May and today got acquired by Wix for $80m..."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://x.com/PrivatEquityGuy/status/1935405148014019002" rel="noopener noreferrer"&gt;@PrivatEquityGuy on X&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Per &lt;a href="https://www.calcalistech.com/ctechnews/article/j7bfdhkor" rel="noopener noreferrer"&gt;Calcalist&lt;/a&gt;, "Shlomo reported that the company had generated a profit of $189,000, nearly double his initial forecast of $100,000." I want to flag what that profit survived, because it is the part that almost broke him.&lt;/p&gt;

&lt;h2&gt;
  
  
  The moment something almost broke
&lt;/h2&gt;

&lt;p&gt;Base44 calls large language models to generate full apps in real time. Every single request costs money. In May, model calls were 89% of Base44's total spend, according to a detailed &lt;a href="https://eu.36kr.com/en/p/3393744947939456" rel="noopener noreferrer"&gt;breakdown via 36Kr&lt;/a&gt; of his public posts.&lt;/p&gt;

&lt;p&gt;That is a business where the cost of goods sold tries to eat you alive. Shlomo's fix was unglamorous. He moved off OpenAI's API to cheaper routes through AWS Bedrock, Google Vertex, and Anthropic, chasing cost-per-performance. He also stopped charging users for failed outputs, which sounds generous until you realize a model that keeps making mistakes just burns more tokens on his dime.&lt;/p&gt;

&lt;p&gt;He leaned on Cursor and Claude for development. He said on Lenny's that he had not written a line of front-end code in three months. The AI wrote it. He architected.&lt;/p&gt;

&lt;h2&gt;
  
  
  The verdict, with the asterisks
&lt;/h2&gt;

&lt;p&gt;In June 2025, Wix bought Base44 for $80 million in cash, with earn-outs running through 2029.&lt;/p&gt;

&lt;p&gt;Now the honesty section, because the clean version of this story is a lie of omission.&lt;/p&gt;

&lt;p&gt;First, "solo" is doing heavy lifting. Wix confirmed to TechCrunch that Base44 had eight employees at the sale, and $25 million of the $80 million was a retention pool for them. Shlomo ran it alone for most of the six months and hired his first employee about six weeks before the deal, but the word "solo" on the marketing materials is a stretch by the finish line.&lt;/p&gt;

&lt;p&gt;Second, the Explorium history matters more than the takes admit. A first-time nobody does not get a warm introduction to Wix's CEO. Shlomo was already known in Israeli tech, he was named to Forbes Israel's 30 Under 30 in 2020, and that network is a real input that you cannot copy by using Cursor.&lt;/p&gt;

&lt;p&gt;Third, and this is the uncomfortable one for anyone treating the $80 million as the moral of the story. The acquisition has been rough on the acquirer. Base44 hit $100 million ARR nine months after the deal, per &lt;a href="https://www.calcalistech.com/ctechnews/article/bkqq0pry11e" rel="noopener noreferrer"&gt;Calcalist&lt;/a&gt;, which is genuinely wild growth. But Calcalist also reports Wix's stock has lost nearly half its value in 2026, and that Base44's compute and marketing costs are a heavy drag on the company that bought it. The thing grows beautifully and bleeds cash at the same time. Both are true.&lt;/p&gt;

&lt;p&gt;For Shlomo personally, it kept paying. Calcalist reports he is &lt;a href="https://www.calcalistech.com/ctechnews/article/hjm11dastwl" rel="noopener noreferrer"&gt;set to receive another $90 million&lt;/a&gt; if milestones hit, on top of the headline price.&lt;/p&gt;

&lt;p&gt;So what is the actual lesson here. It is not "quit and vibe-code an $80 million exit by Friday." It is narrower and more useful. He picked a problem he personally had, twice. He shipped while it was embarrassing and broke often. He watched real users in real time and removed friction. And he understood his cost structure cold, which is the part most AI founders wave away until the GPU bill arrives and the math stops working.&lt;/p&gt;

&lt;p&gt;He told Lenny something that has stuck with me. The best product feedback at $5M ARR was the same as at $150M ARR. People close to you, who feel comfortable telling you the truth. The tooling changed. That part did not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.lennysnewsletter.com/p/the-base44-bootstrapped-startup-success-story-maor-shlomo" rel="noopener noreferrer"&gt;Lenny's Podcast / Newsletter, Maor Shlomo interview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://techcrunch.com/2025/06/18/6-month-old-solo-owned-vibe-coder-base44-sells-to-wix-for-80m-cash/" rel="noopener noreferrer"&gt;TechCrunch, Wix acquires Base44&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/posts/maor-shlomo-1088b4144_base44-ended-up-making-189k-profit-in-may-activity-7336025796509077504-OzSO" rel="noopener noreferrer"&gt;LinkedIn, Shlomo on $189K May profit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.calcalistech.com/ctechnews/article/bkqq0pry11e" rel="noopener noreferrer"&gt;Calcalist, Base44 hits $100M ARR&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.calcalistech.com/ctechnews/article/hjm11dastwl" rel="noopener noreferrer"&gt;Calcalist, Shlomo $90M milestone payout&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.calcalistech.com/ctechnews/article/j7bfdhkor" rel="noopener noreferrer"&gt;Calcalist, Base44 booming, Wix collapsing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://eu.36kr.com/en/p/3393744947939456" rel="noopener noreferrer"&gt;36Kr, cost breakdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ceoinsider.io/interview/maor-shlomo" rel="noopener noreferrer"&gt;CEO Insider interview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://x.com/PrivatEquityGuy/status/1935405148014019002" rel="noopener noreferrer"&gt;X, PrivatEquityGuy citing the $189K post&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>founders</category>
      <category>base44</category>
      <category>wix</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>From Invisible to Indexed: The Unglamorous Work of Getting Seen by AI Search</title>
      <dc:creator>Agnel Nieves</dc:creator>
      <pubDate>Mon, 15 Jun 2026 20:55:43 +0000</pubDate>
      <link>https://dev.to/promptway/from-invisible-to-indexed-the-unglamorous-work-of-getting-seen-by-ai-search-2590</link>
      <guid>https://dev.to/promptway/from-invisible-to-indexed-the-unglamorous-work-of-getting-seen-by-ai-search-2590</guid>
      <description>&lt;p&gt;Last week I discovered that my own publication was invisible on its own domain.&lt;/p&gt;

&lt;p&gt;Not invisible in the poetic, nobody-reads-my-blog way. Literally invisible. promptway.com, the domain every article on the site declared as its canonical home, was serving an old product prototype I had built months earlier and forgotten about. The actual publication lived on a temporary platform URL. Every AI crawler that found an article was being told "the real copy lives over there," and over there was a different product.&lt;/p&gt;

&lt;p&gt;I had written a whole piece about &lt;a href="https://dev.to/blog/optimizing-your-site-for-ai-agents"&gt;optimizing your site for AI agents and LLMs&lt;/a&gt;. The eight layers in that article were all real and all working. And none of it mattered, because the front door had the wrong address on it.&lt;/p&gt;

&lt;p&gt;So I spent a day fixing the unglamorous parts. This is the checklist, in plain words. None of it requires you to be a developer, though some steps need one for an hour or two. If part one was about making your site readable to machines, this is about making sure the machines ever show up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 0: check what your domain actually serves
&lt;/h2&gt;

&lt;p&gt;Open a private browser window and type your domain. Not your bookmark. The domain.&lt;/p&gt;

&lt;p&gt;Then check three URLs by hand: &lt;code&gt;yourdomain.com/sitemap.xml&lt;/code&gt;, &lt;code&gt;yourdomain.com/feed.xml&lt;/code&gt;, and &lt;code&gt;yourdomain.com/robots.txt&lt;/code&gt;. If any of them 404, that is your whole afternoon right there. Mine did. The publication had all three, but the domain was attached to the wrong project, so the live internet got none of them.&lt;/p&gt;

&lt;p&gt;While you are in there, settle the www question. Pick one form of your domain, either &lt;code&gt;yourdomain.com&lt;/code&gt; or &lt;code&gt;www.yourdomain.com&lt;/code&gt;, and make the other one redirect to it permanently. Search engines treat them as two different sites until you do. I picked the bare domain, made www redirect to it, and also made the hosting platform's free preview URL redirect home, because a copy of your site living on a platform subdomain is a duplicate-content problem you are choosing to keep.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: let the right robots in
&lt;/h2&gt;

&lt;p&gt;Your &lt;code&gt;robots.txt&lt;/code&gt; file is the bouncer at the door. The guest list changed a lot recently, and most sites are still working from an old one.&lt;/p&gt;

&lt;p&gt;The crawler that decides whether ChatGPT search cites you is called OAI-SearchBot, and it did not exist when most robots.txt files were written. Same story for Claude-SearchBot, Perplexity-User, and a handful of others. If your file does not mention them, you are relying on default behavior. I would rather be explicit: I allow every major AI search crawler by name, because being cited is the whole point of a publication.&lt;/p&gt;

&lt;p&gt;This is a ten-minute edit. Ask whoever manages your site to compare your allowlist against a current guide to AI user agents and add what is missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: put your whole article in the feed
&lt;/h2&gt;

&lt;p&gt;Here is one I had completely wrong. My RSS feed only carried summaries.&lt;/p&gt;

&lt;p&gt;A summary-only feed feels safe, like you are protecting the full text. What it actually does is cripple every downstream channel. Feed readers show your readers a teaser and a link. Syndication platforms that import via RSS get nothing worth importing. AI systems that ingest feeds get a paragraph instead of the article.&lt;/p&gt;

&lt;p&gt;Full-content feeds are how machines subscribe to you. The fix is technical but small: your RSS feed should carry the complete rendered article, and if you have a JSON Feed, it should too. Your developer will know this as &lt;code&gt;content:encoded&lt;/code&gt;. The conversation takes one sentence: "make our feeds full-content."&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: tell the engines when you publish, do not wait to be found
&lt;/h2&gt;

&lt;p&gt;By default, publishing works like this: you post, and then you wait for a crawler to wander by. That can take days.&lt;/p&gt;

&lt;p&gt;Two free standards flip it to a push model. IndexNow is a single notification you send when a page is new or updated, and one submission covers Bing, Yandex, and a few other engines at once. Bing matters more than its search share suggests, because most of the web results ChatGPT cites overlap heavily with Bing's top results. WebSub does the same job for feed readers: it pings a hub when your feed changes, and subscribers update within seconds instead of whenever they next poll.&lt;/p&gt;

&lt;p&gt;Both are set-and-forget. We wired ours into a small script that runs automatically on publish, and I never think about it. If you publish through a platform like Ghost or WordPress, there is a decent chance a plugin or setting already does this. Turn it on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: put a human name on AI-assisted work
&lt;/h2&gt;

&lt;p&gt;Some of the writing on Promptway is drafted by AI personas. That is disclosed openly, profile pages and all. But disclosure alone is not enough anymore, and the sites that got hammered by Google's scaled-content crackdowns had one thing in common: nobody human was accountable for the words.&lt;/p&gt;

&lt;p&gt;So every persona-written article on the site now carries a visible line: "Reviewed by Agnel Nieves," linking to my profile. It is the same pattern medical sites have used for years with "medically reviewed by Dr. X." The page also says it in structured data, where I am listed as the editor, and my profile is connected to my real accounts elsewhere so machines can verify I am a person who exists.&lt;/p&gt;

&lt;p&gt;One honest note: this is an accountability signal, not a ranking cheat code. It tells readers and crawlers that a named human stands behind the work, which happens to be true. If it were not true, I would fix that first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: never ship a blank share card
&lt;/h2&gt;

&lt;p&gt;Paste one of your article links into Slack or iMessage. If the preview is a gray rectangle, you are losing clicks you already earned.&lt;/p&gt;

&lt;p&gt;Most articles on my site have generated hero art, but a few do not, and those shipped with no preview image at all. The fix was a small template that auto-generates a clean, typographic card with the article title for any post without art. Every link now unfurls into something. Most platforms have this built in; if yours is custom, it is an afternoon of work for a developer, once, forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: measure the AI traffic separately
&lt;/h2&gt;

&lt;p&gt;Here is the stat that convinced me to finally set up analytics: visitors arriving from AI tools like ChatGPT and Perplexity are a tiny slice of traffic, around one percent industry-wide, but they convert far better than organic search visitors. Someone who clicks through from an AI answer chose your site after the machine already summarized you. That is a warm lead, not a drive-by.&lt;/p&gt;

&lt;p&gt;If you lump that traffic in with everything else, you will never see it. The setup is one custom channel group in Google Analytics that buckets referrals from chatgpt.com, perplexity.ai, claude.ai, and gemini.google.com. Ten minutes in the admin panel, and from then on you can answer "is any of this AI optimization work doing anything?" with a number.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part where I admit a fleet of agents did the typing
&lt;/h2&gt;

&lt;p&gt;Full transparency: I did not hand-write most of this. I described the plan, and a team of AI coding agents executed it, each sized to its task. Small models refreshed the robots file and the redirects. Bigger ones rebuilt the feeds and wrote the publish-time automation. I reviewed the output, caught a few real mistakes (one agent excluded the exact files we needed redirected, another drew a logo with the text colliding), fixed them, and shipped.&lt;/p&gt;

&lt;p&gt;That division of labor felt right. The judgment calls, what to build, what to skip, what counted as done, stayed with me. The typing did not have to.&lt;/p&gt;

&lt;h2&gt;
  
  
  The whole checklist, one more time
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Type your domain into a private window and make sure it serves the thing you want found, on one canonical host.&lt;/li&gt;
&lt;li&gt;Check &lt;code&gt;sitemap.xml&lt;/code&gt;, &lt;code&gt;feed.xml&lt;/code&gt;, and &lt;code&gt;robots.txt&lt;/code&gt; by hand. Fix the 404s.&lt;/li&gt;
&lt;li&gt;Update your robots.txt allowlist for the 2026 crawler landscape.&lt;/li&gt;
&lt;li&gt;Make your feeds full-content.&lt;/li&gt;
&lt;li&gt;Wire up IndexNow and WebSub so publishing pushes instead of waiting to be pulled.&lt;/li&gt;
&lt;li&gt;Put a named, linkable human reviewer on anything AI-assisted.&lt;/li&gt;
&lt;li&gt;Generate a fallback share image so no link ever unfurls blank.&lt;/li&gt;
&lt;li&gt;Build an AI-referrals channel group in your analytics.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;None of this is clever. That is the point. The clever work, the writing, the structured data, the llms.txt file, only pays off after the plumbing is sound. Check the front door first.&lt;/p&gt;

</description>
      <category>aeo</category>
      <category>indexing</category>
      <category>feeds</category>
      <category>analytics</category>
    </item>
    <item>
      <title>The De-Slop Prompt Stack: Six Prompts That Stop Claude and ChatGPT From Sounding Like a Robot</title>
      <dc:creator>Agnel Nieves</dc:creator>
      <pubDate>Mon, 15 Jun 2026 20:55:42 +0000</pubDate>
      <link>https://dev.to/promptway/the-de-slop-prompt-stack-six-prompts-that-stop-claude-and-chatgpt-from-sounding-like-a-robot-36ji</link>
      <guid>https://dev.to/promptway/the-de-slop-prompt-stack-six-prompts-that-stop-claude-and-chatgpt-from-sounding-like-a-robot-36ji</guid>
      <description>&lt;p&gt;&lt;a href="https://www.merriam-webster.com/wordplay/word-of-the-year" rel="noopener noreferrer"&gt;Merriam-Webster's 2025 word of the year&lt;/a&gt; wasn't a tech term that crossed over. It was an insult. Slop: roughly, low-quality digital content churned out in volume by AI.&lt;/p&gt;

&lt;p&gt;When the dictionary names the thing your content pipeline produces by default, that's worth twenty minutes of your attention. Here are the twenty minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trust penalty is measurable now
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.canva.com/marketing-ai-report/" rel="noopener noreferrer"&gt;Canva's State of Marketing and AI 2026 report&lt;/a&gt;, a Harris Poll of 1,415 marketing leaders and 3,547 consumers across seven countries released in May, found that 97% of marketing leaders now use AI daily. The same report found that 78% of consumers would rather see ads made by people, that 87% say the best advertising still requires a human touch, and that mentions of "AI slop" were up ninefold.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.bynder.com/en/press-media/ai-vs-human-made-content-study/" rel="noopener noreferrer"&gt;Bynder's Human Touch survey&lt;/a&gt; of 2,000 UK and US consumers adds the twist. 52% said they get less engaged the moment they suspect copy is AI-written. But 56% actually preferred an unlabeled AI-written article over the human copywriter's version.&lt;/p&gt;

&lt;p&gt;Read those two Bynder numbers together. People can't reliably detect good AI writing. They reliably punish detectable AI writing. The penalty isn't for using the tool. It's for the tells.&lt;/p&gt;

&lt;p&gt;So the job is simple to state: remove the tells.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tells, so you can delete them on sight
&lt;/h2&gt;

&lt;p&gt;Before the prompts, the symptom list. Machine-default writing is recognizable because it always fails the same ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Metronome rhythm. Every sentence runs 15 to 20 words, forever.&lt;/li&gt;
&lt;li&gt;The connector parade: furthermore, moreover, additionally, consequently.&lt;/li&gt;
&lt;li&gt;Giveaway vocabulary: delve, tapestry, landscape, elevate, crucial, robust, journey.&lt;/li&gt;
&lt;li&gt;"It's not just X. It's Y." Fine once. A tell when it's every third paragraph.&lt;/li&gt;
&lt;li&gt;Threes. Everything arrives in threes.&lt;/li&gt;
&lt;li&gt;Confident claims with no source attached.&lt;/li&gt;
&lt;li&gt;Em dashes every other sentence. We ban them at Promptway entirely, and the models adore them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No single item proves anything. Stacked together, they're the smell. The six prompts below kill them at the source.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;p&gt;The first three are setup prompts. Run them before drafting, or better, save them permanently (instructions at the end). The last three are editing passes you run on a finished draft.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The rhythm and banned-words prompt
&lt;/h3&gt;

&lt;p&gt;The two loudest tells are vocabulary and rhythm, so handle both in one standing instruction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# The rhythm and banned-words rules
Apply these writing rules to everything in this conversation:

Never use these words: delve, tapestry, landscape, elevate, crucial,
robust, seamless, journey, realm, leverage, unlock, harness,
game-changing, transformative.

Never open with "In today's" or "In the world of."

No "furthermore," "moreover," "additionally," or "consequently."
Connect ideas the way people talk: "and," "but," "so," or just start
the next sentence.

No em dashes anywhere. Use a period, a comma, or parentheses.

Vary sentence length on purpose. Some sentences under six words.
Some over twenty. If three sentences in a row share the same
structure, rewrite the third one.

Maximum one exclamation point per piece. Prefer zero.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why it works: models respond far better to explicit negatives than to "sound more human," which is too vague to act on. What goes wrong: in long conversations the rules drift. Repaste them, or save them permanently so you never have to.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The voice transplant
&lt;/h3&gt;

&lt;p&gt;The default model voice is generic-competent, which means it belongs to nobody. Give it yours:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# The voice transplant
Below are samples of my actual writing, about 400 words total.
Study how long my sentences run, which words I repeat, how I open
and close paragraphs, and how often I use contractions.

[PASTE 300 TO 500 WORDS OF YOUR OWN WRITING]

Write the piece I describe next in that voice. Do not reuse my
sentences or my topics. Match the rhythm and the vocabulary level.
If you are not sure whether I would say a phrase, leave it out.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use writing you produced without trying to impress anyone. Emails to colleagues work better than your polished blog posts. What goes wrong: a sample under 200 words produces a caricature instead of a voice. And the last line matters, because "leave it out" stops the model from filling gaps with its defaults.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The coffee test
&lt;/h3&gt;

&lt;p&gt;Our writing guide's oldest rule is to read every draft out loud. This prompt builds the rule into the machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# The coffee test
Rewrite this draft as if you were explaining it to a smart friend
over coffee. Contractions are fine. Sentence fragments are fine.
Keep every fact. Cut every sentence that sounds like a press
release. The test: if a sentence would sound stiff read out loud,
rewrite it until it doesn't.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one earns its spot on tone alone. It converts announcements into explanations, which is most of what "sounding human" means.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The specificity pass
&lt;/h3&gt;

&lt;p&gt;Vagueness is slop's natural register, and it's also where AI writing quietly lies. This pass fixes both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# The specificity pass
Go through this draft and replace every vague claim with a concrete
one. "Saves time" becomes a number of minutes or hours. "Many
companies" becomes a count, a sourced percentage, or one named
example. "Recently" becomes a month and a year.

Important: if you do not have the real specific, do not invent one.
Write [NEED SPECIFIC] in its place so I can fill it in myself.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The [NEED SPECIFIC] line is the load-bearing part. Demand specifics without it and the model will manufacture them. A marked gap is useful. An invented number is a liability with your name on it.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. The honest objection
&lt;/h3&gt;

&lt;p&gt;Human writing argues with itself a little. Default AI writing never does, which is part of why it reads like a brochure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# The honest objection
Find the weakest claim in this draft. At roughly the halfway point,
add the strongest objection a skeptical reader would raise against
it, then answer the objection honestly in two or three sentences.
If the objection partly wins, concede that part. Do not strawman
the reader.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A conceded point reads more human, and more trustworthy, than a parade of wins. It also forces you to know which of your claims is the weak one, which is worth knowing before your readers find it for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. The true-anecdote opener
&lt;/h3&gt;

&lt;p&gt;Nothing reads more human than a real, specific moment. Nothing reads more fake than an invented one, so this prompt comes with a fence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# The true-anecdote opener
Here is something that actually happened: [DESCRIBE THE REAL MOMENT
IN TWO OR THREE SENTENCES: who, where, what went wrong or what
surprised you].

Open the piece with a short scene built only from those details.
Do not add details I did not give you. Keep the scene under 80
words, then move into the main point.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ask a model for "an engaging opening story" and you get confident fiction. Feed it a real moment and restrict it to that moment, and you get the one thing slop can never fake: something that happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Save it once, stop repasting
&lt;/h2&gt;

&lt;p&gt;Don't run this by hand every morning. Prompts 1 through 3 belong in your standing instructions: custom instructions or a Project in ChatGPT, and your project's custom instructions in Claude. Every new chat then starts with your rules and your voice already loaded. Prompts 4 through 6 work best as an editing pass you run on finished drafts before anything ships.&lt;/p&gt;

&lt;p&gt;This is the same logic as &lt;a href="https://dev.to/blog/the-constraint-goes-first"&gt;putting your constraints at the top of the prompt&lt;/a&gt;: the rules you cannot afford to lose should be the first thing the model reads, every time, without you having to remember them.&lt;/p&gt;

&lt;p&gt;Total setup time is about fifteen minutes, once.&lt;/p&gt;

&lt;h2&gt;
  
  
  What these prompts will not do
&lt;/h2&gt;

&lt;p&gt;They fix tone. They do not fix truth.&lt;/p&gt;

&lt;p&gt;A model can produce a beautifully rhythmic, perfectly voice-matched paragraph built around a wrong number. Verify every figure, name, date, and quote before you ship, every time. Slop that's also wrong is worse than slop.&lt;/p&gt;

&lt;p&gt;And keep humans on the pieces where the reader needs to feel a person on the other end: the apology, the crisis note, the founder letter. The Canva data says people don't hate AI content as much as they want to feel that a human took responsibility for it. On some pieces, that feeling is the content.&lt;/p&gt;

&lt;h2&gt;
  
  
  The point was never the detector
&lt;/h2&gt;

&lt;p&gt;AI detectors are coin flips anyway, so fooling them was never a goal worth having. The goal is to stop wasting your reader's attention on writing that sounds like everyone else's default settings.&lt;/p&gt;

&lt;p&gt;Run the stack, then read the draft out loud. If it sounds like a person, ship it.&lt;/p&gt;

</description>
      <category>prompting</category>
      <category>aiwriting</category>
      <category>slop</category>
      <category>claude</category>
    </item>
    <item>
      <title>Rust Owns the JavaScript Toolchain in 2026</title>
      <dc:creator>Agnel Nieves</dc:creator>
      <pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/agnelnieves/rust-owns-the-javascript-toolchain-in-2026-l9b</link>
      <guid>https://dev.to/agnelnieves/rust-owns-the-javascript-toolchain-in-2026-l9b</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vzmnz9w2ns2afgdxc0l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vzmnz9w2ns2afgdxc0l.png" alt="abstract lines artwork" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;If you are shipping a modern JavaScript app in 2026, almost every step between your editor and your production bundle is a Rust binary. Next.js builds with Turbopack. Vite builds with Rolldown. Your linter and formatter are Biome or oxlint. Your CSS pipeline runs through Lightning CSS. Bun, the runtime that started in Zig, just merged its Rust rewrite into main. The result: bundlers are 5 to 30x faster, linters are 50 to 100x faster, and the dependency tree of "the JavaScript toolchain" has collapsed to a handful of statically linked binaries with zero npm postinstall scripts. That last detail matters more than it sounds, because the npm supply chain spent the last 18 months getting set on fire.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I'm writing this
&lt;/h2&gt;

&lt;p&gt;I noticed it in pieces. I shipped &lt;a href="https://agnelnieves.com/blog/building-a-terminal-portfolio-you-can-ssh-into" rel="noopener noreferrer"&gt;a terminal portfolio over SSH&lt;/a&gt; in Rust two weeks ago, and the build pipeline felt like a different planet to anything I had touched in Node years ago. Then &lt;a href="https://nextjs.org/blog/next-16" rel="noopener noreferrer"&gt;Next.js 16&lt;/a&gt; went stable with Turbopack as the default. Then &lt;a href="https://vite.dev/blog/announcing-vite8" rel="noopener noreferrer"&gt;Vite 8&lt;/a&gt; shipped with Rolldown. Then &lt;a href="https://github.com/oven-sh/bun/pull/30412" rel="noopener noreferrer"&gt;Anthropic merged a Zig-to-Rust port of Bun&lt;/a&gt;, mostly written with AI. Then a fresh &lt;a href="https://www.aikido.dev/blog/mini-shai-hulud-is-back-tanstack-compromised" rel="noopener noreferrer"&gt;Mini Shai-Hulud wave&lt;/a&gt; hit TanStack, Mistral, and 160+ npm packages three days ago, on the heels of a &lt;a href="https://www.wiz.io/blog/shai-hulud-2-0-ongoing-supply-chain-attack" rel="noopener noreferrer"&gt;November Shai-Hulud campaign&lt;/a&gt; that compromised roughly 25,000 GitHub repos. "Small dependency" is now a polite phrase for "unsandboxed code with full filesystem access."&lt;/p&gt;

&lt;p&gt;Three threads, one direction. Worth writing down.&lt;/p&gt;

&lt;p&gt;I am late to this take. Lee Robinson wrote &lt;a href="https://leerob.com/rust" rel="noopener noreferrer"&gt;"Rust Is Eating JavaScript"&lt;/a&gt; and the 2026 update made the bigger point cleanly. The angles I want to add are mobile and security, because the toolchain story is only the first act.&lt;/p&gt;

&lt;h2&gt;
  
  
  The toolchain went Rust
&lt;/h2&gt;

&lt;p&gt;Pick a step in the build pipeline and chances are it has been quietly rewritten in Rust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Turbopack.&lt;/strong&gt; &lt;a href="https://nextjs.org/blog/next-16" rel="noopener noreferrer"&gt;Next.js 16&lt;/a&gt; (February 2026) shipped Turbopack as the default bundler for both &lt;code&gt;next dev&lt;/code&gt; and &lt;code&gt;next build&lt;/code&gt;. The numbers Vercel published before the cutover: more than half of all dev sessions and more than a fifth of production builds were already on Turbopack via 15.3+. After the default flipped, the 16.2 release in April claimed roughly 87% faster dev startup on real apps. The HMR feels different. I had stopped noticing how often I was alt-tabbing during compiles, and after switching I noticed how often I was not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rolldown.&lt;/strong&gt; &lt;a href="https://vite.dev/blog/announcing-vite8" rel="noopener noreferrer"&gt;Vite 8&lt;/a&gt; shipped on March 12, 2026 with &lt;a href="https://voidzero.dev/posts/announcing-rolldown-1-0" rel="noopener noreferrer"&gt;Rolldown&lt;/a&gt; as the bundler, taking over from the Rollup plus esbuild combination. Public production numbers from the Rolldown team: Excalidraw dropped from 22.9s to 1.4s. PLAID, an internal product at ByteDance, dropped from 80s to 5s. Both are roughly 16x. The transitional &lt;code&gt;rolldown-vite&lt;/code&gt; package was archived a week after 8.0 because it was redundant. If you &lt;code&gt;npm create vite@latest&lt;/code&gt; today, you get Rolldown without asking for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rspack.&lt;/strong&gt; ByteDance's webpack-compatible &lt;a href="https://www.infoq.com/news/2026/01/rspack-final-rust/" rel="noopener noreferrer"&gt;Rust bundler hit 1.7 in January&lt;/a&gt; and is on the runway to 2.0. Internally they ship TikTok, Douyin, Lark, and Coze on it. Externally, Microsoft, Amazon, and Discord are in the public adopter list. The migration cases I have seen all land in the same neighborhood: 9x faster cold dev, 3x less memory, prod builds under 4 seconds on apps that used to take 30.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Biome and oxlint.&lt;/strong&gt; ESLint is no longer the default lint stack. &lt;a href="https://biomejs.dev/blog/biome-v2/" rel="noopener noreferrer"&gt;Biome 2&lt;/a&gt; added type-aware rules that do not require running the TypeScript compiler. &lt;a href="https://oxc.rs/blog/2025-06-10-oxlint-stable" rel="noopener noreferrer"&gt;Oxlint 1.0 went stable in August&lt;/a&gt;, ran 50 to 100x faster than ESLint on the same rulesets, and shipped a &lt;a href="https://voidzero.dev/posts/announcing-oxlint-type-aware-linting-alpha" rel="noopener noreferrer"&gt;type-aware alpha&lt;/a&gt; in March on top of &lt;code&gt;tsgolint&lt;/code&gt; (which itself runs on Microsoft's Go-based &lt;code&gt;tsgo&lt;/code&gt;). Shopify reported a 71% lint-time reduction across an ~80,000 file TypeScript codebase. The pattern teams actually use: oxlint as a pre-commit and CI gate for the hot rules, Biome for formatting, ESLint kept around for any plugin you cannot get rid of yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lightning CSS.&lt;/strong&gt; &lt;a href="https://tailwindcss.com/blog/tailwindcss-v4" rel="noopener noreferrer"&gt;Tailwind v4&lt;/a&gt;'s "Oxide" engine is &lt;a href="https://github.com/parcel-bundler/lightningcss" rel="noopener noreferrer"&gt;Lightning CSS&lt;/a&gt; underneath. Full builds 5x faster, incremental builds up to 100x. The PostCSS chain is gone from the default setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bun.&lt;/strong&gt; Bun was a Zig project. In May, Anthropic (which acquired Bun last year and uses it inside Claude Code) &lt;a href="https://github.com/oven-sh/bun/pull/30412" rel="noopener noreferrer"&gt;merged a port of the entire codebase from Zig to Rust&lt;/a&gt; into main. The port is roughly 966,000 lines, it was largely AI-assisted, and it passes 99.8% of the existing test suite on Linux x64 glibc. Bun 1.3.14 is positioned as the last Zig release. The reasons given: Rust's memory safety story, and Zig's no-AI contribution policy clashing with how Anthropic builds tooling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deno and Node.&lt;/strong&gt; Deno's core was always Rust, and the &lt;a href="https://deno.com/blog/v2.6" rel="noopener noreferrer"&gt;2.6 release in December&lt;/a&gt; bumped V8 to 14.2, added &lt;code&gt;dx&lt;/code&gt; to replace &lt;code&gt;npx&lt;/code&gt;, and started using &lt;code&gt;tsgo&lt;/code&gt; for type checking. Node 25.2 (November 2025) shipped stable TypeScript support through &lt;a href="https://github.com/nodejs/amaro" rel="noopener noreferrer"&gt;Amaro 1.0&lt;/a&gt;, which is a wrapper around &lt;code&gt;@swc/wasm-typescript&lt;/code&gt;. Node's official type-stripping path is now SWC compiled to WebAssembly, shipping inside core. There is more Rust running inside &lt;code&gt;node&lt;/code&gt; than most teams realize.&lt;/p&gt;

&lt;p&gt;If you list the build steps in your CI pipeline (lint, format, type check, transpile, bundle, minify, optimize CSS) the only step still being done by JavaScript on JavaScript code in 2026 is type checking. And the most consequential type checker (Microsoft's official one) is &lt;a href="https://devblogs.microsoft.com/typescript/typescript-native-port/" rel="noopener noreferrer"&gt;being ported to Go&lt;/a&gt;, not Rust, by the team that wrote TypeScript in the first place. That is the one part of the ecosystem where the obvious "rewrite it in Rust" bet did not pay off, and it is worth saying out loud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bigger than dev tools
&lt;/h2&gt;

&lt;p&gt;This is bigger than JavaScript. Microsoft has &lt;a href="https://azure.microsoft.com/en-us/blog/microsoft-azure-security-evolution-embrace-secure-multitenancy-confidential-compute-and-rust/" rel="noopener noreferrer"&gt;publicly committed to embracing Rust across Azure infrastructure&lt;/a&gt;, and Azure CTO Mark Russinovich has spent the last two years telling every conference audience that new kernel development at Microsoft should stop using C or C++. New code for Windows and Azure should be written in Rust. That is not a future commitment. Production components already moved: parts of &lt;code&gt;Win32k.sys&lt;/code&gt;, Hyper-V, the &lt;a href="https://www.microsoft.com/en-us/research/blog/rewriting-symcrypt-in-rust-to-modernize-microsofts-cryptographic-library/" rel="noopener noreferrer"&gt;SymCrypt cryptographic library&lt;/a&gt;, and Azure Data Explorer. The &lt;a href="https://devblogs.microsoft.com/azure-sdk/azure-sdk-release-march-2026/" rel="noopener noreferrer"&gt;Azure SDK for Rust&lt;/a&gt; shipped beta in February 2025 and now releases monthly alongside the other language SDKs. At &lt;a href="https://thenewstack.io/microsoft-goes-all-in-on-rust-for-core-infrastructure-and-much-more/" rel="noopener noreferrer"&gt;RustConf 2025&lt;/a&gt;, Microsoft's Galen Hunt described the broader internal goal as refactoring roughly one million lines of code per month for the rest of the decade, with the aim of eliminating C and C++ from Microsoft's codebase by 2030.&lt;/p&gt;

&lt;p&gt;The Linux kernel has merged Rust drivers since 6.1 (late 2022) and the &lt;a href="https://rust-for-linux.com/" rel="noopener noreferrer"&gt;Rust-for-Linux subsystem keeps growing each release&lt;/a&gt;. AWS rewrote &lt;a href="https://github.com/firecracker-microvm/firecracker" rel="noopener noreferrer"&gt;Firecracker&lt;/a&gt;, its microVM monitor, in Rust years ago and continues to add Rust services across the stack. Google has Rust in &lt;a href="https://security.googleblog.com/2024/09/eliminating-memory-safety-vulnerabilities-Android.html" rel="noopener noreferrer"&gt;Android&lt;/a&gt;, in parts of Chromium, and in Fuchsia. When the kernel your build tools run on is moving to Rust, the build tools moving to Rust is the predictable next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about mobile
&lt;/h2&gt;

&lt;p&gt;Mobile is a different shape and Rust shows up differently.&lt;/p&gt;

&lt;p&gt;The default cross-platform stacks (React Native, Flutter) are not Rust. React Native's New Architecture (Fabric, TurboModules, JSI) is C++. Hermes is C++. Flutter is Dart plus C++. What changed in the last 18 months is the layer just above that: Rust as a shared core, exposed to those frameworks through binding generators.&lt;/p&gt;

&lt;p&gt;The pattern teams actually use: &lt;strong&gt;write the things that should not be rewritten twice in Rust, and put a thin native UI on top of them.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Signal&lt;/strong&gt; does this with &lt;a href="https://github.com/signalapp/libsignal" rel="noopener noreferrer"&gt;&lt;code&gt;libsignal&lt;/code&gt;&lt;/a&gt;. The protocol, AES-GCM and other primitives, zero-knowledge groups, and remote attestation are all Rust crates. Java, Swift, and TypeScript wrappers expose them to Android, iOS, and Desktop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1Password&lt;/strong&gt; rewrote its core (sync, storage, crypto, permissions) in Rust, kept native UI per platform, and built &lt;a href="https://1password.com/blog/typeshare-for-rust" rel="noopener noreferrer"&gt;TypeShare&lt;/a&gt; so type definitions stay consistent across the FFI boundary. One Rust core, eight clients on top.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mozilla&lt;/strong&gt; ships sync, login storage, browsing history, push, and experimentation as Rust components shared between Firefox iOS and Firefox Android, all generated through &lt;a href="https://github.com/mozilla/uniffi-rs" rel="noopener noreferrer"&gt;UniFFI&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare&lt;/strong&gt; ships &lt;a href="https://github.com/cloudflare/boringtun" rel="noopener noreferrer"&gt;BoringTun&lt;/a&gt;, a userspace WireGuard implementation in Rust, on millions of consumer iOS and Android devices through Mozilla VPN and others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bitwarden&lt;/strong&gt; is moving its cryptographic operations into a shared &lt;code&gt;bitwarden_core&lt;/code&gt; Rust SDK consumed by every client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The new bit, and the reason this thread is worth pulling on right now: in December 2024 Mozilla and Filament released &lt;a href="https://hacks.mozilla.org/2024/12/introducing-uniffi-for-react-native-rust-powered-turbo-modules/" rel="noopener noreferrer"&gt;&lt;strong&gt;Uniffi for React Native&lt;/strong&gt;&lt;/a&gt;. It generates a TurboModule (TypeScript plus the JSI C++ glue) directly from a Rust crate and a UniFFI interface definition. That finally aligns React Native with the pattern Signal and 1Password have been using productively for years. Flutter has had &lt;a href="https://github.com/fzyzcjy/flutter_rust_bridge" rel="noopener noreferrer"&gt;&lt;code&gt;flutter_rust_bridge&lt;/code&gt;&lt;/a&gt; doing the same job for a while, and v2 is now an officially Flutter Favorite package with async Rust support, web targets, and zero-copy big arrays.&lt;/p&gt;

&lt;p&gt;The Rust-native UI frameworks (Dioxus, Slint, egui) are real and improving. &lt;a href="https://slint.dev/blog/slint-1.12-released" rel="noopener noreferrer"&gt;Slint added an iOS tech preview in 1.12&lt;/a&gt; last June, with proper Xcode integration, simulator and device deploy, TestFlight, and App Store publishing. Dioxus 0.7 runs on iOS reasonably well and on Android with some pain (Huawei and Airbus are public production references). None of these are what I would reach for to build a polished consumer app today, but the trade is no longer "no Rust GUI on phones." It is "yes, but you should know what you are signing up for."&lt;/p&gt;

&lt;p&gt;The honest tradeoffs on mobile have not gone away.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Toolchain pain.&lt;/strong&gt; NDK plus &lt;code&gt;cargo-ndk&lt;/code&gt; for Android. Xcode, codesigning, provisioning, and a sometimes fragile &lt;code&gt;lipo&lt;/code&gt;/XCFramework dance for iOS. Every Rust target multiplies your CI matrix.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;String impedance.&lt;/strong&gt; Rust is UTF-8, Swift is UTF-16, Java/Kotlin is "modified UTF-8." Conversions are easy to get wrong, and "wrong" means corruption or crashes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FFI overhead is real.&lt;/strong&gt; The well-worn guidance: batch your calls. One call processing 100 items beats 100 calls processing one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Binary size.&lt;/strong&gt; A naive Rust core can add MB to your IPA or APK before optimization. LTO, &lt;code&gt;panic=abort&lt;/code&gt;, and symbol stripping are the price of entry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform API drift.&lt;/strong&gt; Widgets, App Intents, Live Activities, Material You, and the latest media APIs almost always live outside the Rust core, in native code. Apple and Google ship features faster than any binding generator can chase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The takeaway for mobile is the same as on the desktop side: Rust as a shared core works. Rust as the whole app on mobile is niche.&lt;/p&gt;

&lt;h2&gt;
  
  
  And then there is npm
&lt;/h2&gt;

&lt;p&gt;This is the part of the story I do not think gets told enough.&lt;/p&gt;

&lt;p&gt;The last 18 months were brutal for the npm registry. Three events worth grounding the rest of this on:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;September 8, 2025: &lt;a href="https://www.aikido.dev/blog/npm-debug-and-chalk-packages-compromised" rel="noopener noreferrer"&gt;qix / chalk + debug&lt;/a&gt;.&lt;/strong&gt; A maintainer named Josh Junon (handle &lt;code&gt;qix&lt;/code&gt;) owns some of the most foundational packages in JavaScript: &lt;code&gt;chalk&lt;/code&gt;, &lt;code&gt;debug&lt;/code&gt;, &lt;code&gt;ansi-styles&lt;/code&gt;, &lt;code&gt;strip-ansi&lt;/code&gt;, &lt;code&gt;color-convert&lt;/code&gt;, and a dozen more. He received a phishing email from &lt;code&gt;support@npmjs.help&lt;/code&gt;, a lookalike domain registered three days earlier. The attacker captured his credentials and TOTP through an adversary-in-the-middle session and published malicious versions of 18 packages with combined weekly downloads of roughly 2 billion. &lt;a href="https://www.exiger.com/perspectives/a-single-compromise-threatened-34-percent-of-npm/" rel="noopener noreferrer"&gt;Exiger estimated the transitive blast radius&lt;/a&gt; at around 34% of the entire npm registry. The payload was a browser-side crypto-clipper that hooked &lt;code&gt;window.ethereum&lt;/code&gt;, &lt;code&gt;window.solana&lt;/code&gt;, &lt;code&gt;fetch&lt;/code&gt;, and &lt;code&gt;XHR&lt;/code&gt;, then swapped wallet addresses in outgoing transactions using minimum Levenshtein distance so the substitution would survive a quick visual check. Aikido flagged it within five minutes. &lt;a href="https://www.sygnia.co/threat-reports-and-advisories/npm-supply-chain-attack-september-2025/" rel="noopener noreferrer"&gt;The malicious versions were on the registry&lt;/a&gt; and being installed by CI worldwide for the entire window before that. Vercel published a same-day &lt;a href="https://vercel.com/blog/critical-npm-supply-chain-attack-response-september-8-2025" rel="noopener noreferrer"&gt;advisory&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;September 15, 2025: &lt;a href="https://unit42.paloaltonetworks.com/npm-supply-chain-attack/" rel="noopener noreferrer"&gt;Shai-Hulud&lt;/a&gt;.&lt;/strong&gt; A week later, &lt;code&gt;@ctrl/tinycolor&lt;/code&gt; (2M+ weekly downloads) was found to contain a self-replicating worm. The malware was the first true worm in the npm ecosystem: every install used the victim's npm token to enumerate other packages the maintainer owned, injected a Webpack-bundled payload, bumped the version, and republished. By the time &lt;a href="https://www.cisa.gov/news-events/alerts/2025/09/23/widespread-supply-chain-compromise-impacting-npm-ecosystem" rel="noopener noreferrer"&gt;CISA issued its alert on September 23&lt;/a&gt;, more than 500 packages were affected, including ones from CrowdStrike, &lt;code&gt;@nativescript-community&lt;/code&gt;, and &lt;code&gt;@operato&lt;/code&gt;. The payload ran TruffleHog against the host to scrape AWS keys, GCP credentials, Azure tokens, GitHub PATs, and npm tokens, then uploaded them to public GitHub repos named "Shai-Hulud." &lt;a href="https://www.wiz.io/blog/shai-hulud-2-0-ongoing-supply-chain-attack" rel="noopener noreferrer"&gt;A second wave on November 24&lt;/a&gt; moved its hook from &lt;code&gt;postinstall&lt;/code&gt; to &lt;code&gt;preinstall&lt;/code&gt; (so it ran before any user-visible install output) and hit roughly 25,000 GitHub repositories. &lt;a href="https://www.kb.cert.org/vuls/id/534320" rel="noopener noreferrer"&gt;CERT/CC VU#534320&lt;/a&gt; called it the first credential-stealing, self-propagating worm in npm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;May 11, 2026: &lt;a href="https://www.aikido.dev/blog/mini-shai-hulud-is-back-tanstack-compromised" rel="noopener noreferrer"&gt;Mini Shai-Hulud&lt;/a&gt;.&lt;/strong&gt; Three days ago, as I write this. A threat actor StepSecurity has been tracking as TeamPCP &lt;a href="https://tanstack.com/blog/npm-supply-chain-compromise-postmortem" rel="noopener noreferrer"&gt;hit TanStack hard&lt;/a&gt;: 42 packages, 84 malicious versions. The same campaign caught Mistral AI, Guardrails AI, UiPath, OpenSearch, and the Bitwarden CLI. Socket flagged 416 affected packages in total; Aikido catalogued 373 malicious package-version entries across 169 names. The technique was novel: a &lt;code&gt;pull_request_target&lt;/code&gt; workflow that ran attacker-controlled fork code, GitHub Actions cache poisoning across the fork/base trust boundary, and OIDC token extraction from the runner's process memory. Once inside maintainer CI, the worm did what its predecessor did: scrape credentials, find publishable packages, inject, republish.&lt;/p&gt;

&lt;p&gt;The detail that mattered most: the malicious TanStack packages were &lt;a href="https://www.stepsecurity.io/blog/mini-shai-hulud-is-back-a-self-spreading-supply-chain-attack-hits-the-npm-ecosystem" rel="noopener noreferrer"&gt;the first documented npm malware shipping with valid SLSA provenance attestations&lt;/a&gt;. The cryptographic chain worked perfectly. The attacker had simply stolen the GitHub OIDC token used to sign builds. Provenance proves "this package was built by this CI run." It does not prove the CI run was not compromised.&lt;/p&gt;

&lt;p&gt;The earlier baseline (the December 2023 &lt;a href="https://www.ledger.com/blog/security-incident-report" rel="noopener noreferrer"&gt;Ledger &lt;code&gt;connect-kit&lt;/code&gt; attack&lt;/a&gt; that drained DeFi front-ends for about five hours, the &lt;a href="https://www.sonatype.com/blog/lottie-player-compromised-in-supply-chain-attack-all-you-need-to-know" rel="noopener noreferrer"&gt;LottieFiles compromise in October 2024&lt;/a&gt;, the &lt;a href="https://www.sonatype.com/blog/npm-packages-rspack-vant-compromised-blocked-by-sonatype" rel="noopener noreferrer"&gt;Rspack token theft in December 2024&lt;/a&gt;) is what made September 2025 land as a confirmation, not a surprise. May 2026 made it a pattern.&lt;/p&gt;

&lt;p&gt;The structural reason these keep happening: &lt;strong&gt;npm is built on install-time arbitrary code execution and ambient authority.&lt;/strong&gt; The &lt;code&gt;preinstall&lt;/code&gt;, &lt;code&gt;install&lt;/code&gt;, and &lt;code&gt;postinstall&lt;/code&gt; hooks run before any human can review them, with full read and write filesystem access and full network. A typical app's &lt;code&gt;node_modules&lt;/code&gt; is several thousand packages from several hundred maintainers, any one of whom can phish or token-leak their way into your build. A logger and a color formatter have the same operating-system privileges as your application code, because Node has no capability-based security to draw a line between them.&lt;/p&gt;

&lt;p&gt;The protective measures that shipped (npm provenance, expanded required 2FA in October and November 2025, scan tools like Socket and OSV-Scanner) help at the margins but do not change the model. A phished maintainer with a valid provenance signature still ships valid signed malware. Mini Shai-Hulud proved that this week, with attestations the GitHub Actions runner generated honestly while compromised. 2FA does not stop an adversary-in-the-middle proxy. Detection time has tightened from days to minutes, which is real progress, but the first thousand installs of a poisoned package still happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this makes Rust tooling load-bearing
&lt;/h2&gt;

&lt;p&gt;Moving the build toolchain to Rust does not fix the structural problems in your runtime dependencies. Your app's &lt;code&gt;node_modules&lt;/code&gt; is still a forest of trust assumptions. What it does change is that the &lt;strong&gt;build&lt;/strong&gt; toolchain (the most-installed, most-privileged surface area in the average project) leaves the npm graph entirely.&lt;/p&gt;

&lt;p&gt;A linter, a formatter, a bundler, a test runner, and a transpiler are pure dev-time tools with read access to your whole codebase and write access to your machine. They are also among the most-installed packages on npm, which is exactly what made their maintainers attractive phishing targets. Replacing them with one statically linked Rust binary collapses that surface. There is no &lt;code&gt;postinstall&lt;/code&gt;. There is no transitive graph. There is a single maintainer organization whose key you can pin.&lt;/p&gt;

&lt;p&gt;Concrete numbers from a clean &lt;code&gt;npm install&lt;/code&gt; I ran this week:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Toolchain choice&lt;/th&gt;
&lt;th&gt;Top-level deps&lt;/th&gt;
&lt;th&gt;Total packages&lt;/th&gt;
&lt;th&gt;Disk&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;eslint&lt;/code&gt; + &lt;code&gt;prettier&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;59&lt;/td&gt;
&lt;td&gt;77&lt;/td&gt;
&lt;td&gt;20 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@biomejs/biome&lt;/code&gt; (Rust)&lt;/td&gt;
&lt;td&gt;1 platform binary wrapper&lt;/td&gt;
&lt;td&gt;1 user-facing&lt;/td&gt;
&lt;td&gt;48 MB binary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rollup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;4.5 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;rolldown&lt;/code&gt; (Rust)&lt;/td&gt;
&lt;td&gt;3 platform binary wrapper&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;19 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Biome's own docs report that a realistic ESLint + Prettier + typical plugins setup lands closer to 127 to 200 packages once plugin ecosystems get involved. The 59 above is the bare minimum.&lt;/p&gt;

&lt;p&gt;Cargo has its own supply-chain risks (typo-squats, the long-running debate about &lt;code&gt;serde&lt;/code&gt;'s precompiled binaries, the occasional malicious crate). What it does not have is a postinstall hook that fires arbitrary code on every developer machine for every transitive build dependency, on every install, forever. That is the difference that matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Rust specifically
&lt;/h2&gt;

&lt;p&gt;Two reasons. The performance numbers are the easy half: 5 to 30x for bundlers, 50 to 100x for linters, 3 to 4x lower memory across the board. That alone would explain the move.&lt;/p&gt;

&lt;p&gt;The harder half is everything else.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Memory safety without a GC.&lt;/strong&gt; Long-running dev servers and HMR processes do not want to pause for 200ms at the wrong moment. GC-based runtimes (Go, JS) hit this; Rust does not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single-binary distribution.&lt;/strong&gt; CLIs like &lt;code&gt;oxlint&lt;/code&gt; and &lt;code&gt;biome&lt;/code&gt; ship as one executable per platform. No Node bootstrap, no thousand &lt;code&gt;require()&lt;/code&gt; calls, no JS launcher script wrapping a native binary wrapping the actual logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fearless parallelism.&lt;/strong&gt; &lt;code&gt;rayon&lt;/code&gt; and &lt;code&gt;tokio&lt;/code&gt; let linters and bundlers fan out across cores cleanly. The JS event loop is fast but it is one core at a time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebAssembly as an escape hatch.&lt;/strong&gt; Node 25.2 ships SWC as wasm inside core. Browser playgrounds run Oxc directly. The same Rust crate can serve a CLI, a Node API, and a browser sandbox without rewriting any of the hot path.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is also worth saying clearly: Rust is not the only winner. TypeScript's compiler is &lt;a href="https://devblogs.microsoft.com/typescript/typescript-native-port/" rel="noopener noreferrer"&gt;being ported to &lt;strong&gt;Go&lt;/strong&gt;&lt;/a&gt;, not Rust, by Anders Hejlsberg and the team that wrote TypeScript in the first place. &lt;a href="https://visualstudiomagazine.com/articles/2026/04/21/typescript-7-0-beta-arrives-on-go-based-foundation-with-10x-speed-claim.aspx" rel="noopener noreferrer"&gt;The 7.0 beta dropped on April 21, 2026&lt;/a&gt; with a 10x speed claim. Bun started in &lt;strong&gt;Zig&lt;/strong&gt; and only just switched. The honest story is "native code ate JS tooling, mostly Rust, but not entirely."&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would build with this stack today
&lt;/h2&gt;

&lt;p&gt;If I were starting a new app this week:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;next@16&lt;/code&gt; or &lt;code&gt;vite@8&lt;/code&gt; for the framework. Both Rust bundlers underneath.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@biomejs/biome&lt;/code&gt; for formatting plus a lint baseline. &lt;code&gt;oxlint&lt;/code&gt; as a CI gate for the type-aware rules.&lt;/li&gt;
&lt;li&gt;Tailwind v4 with the Oxide engine.&lt;/li&gt;
&lt;li&gt;A Rust core library for anything cryptographic, anything sync-heavy, or anything I would want to share between web and mobile, exposed through UniFFI or &lt;code&gt;flutter_rust_bridge&lt;/code&gt; if mobile is on the roadmap.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bun&lt;/code&gt; or &lt;code&gt;pnpm&lt;/code&gt; as the package manager. Lockfile discipline matters more than the manager you pick.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole pipeline would have one or two Node processes that exist mainly to host the dev server and run user code. Everything else underneath would be Rust binaries doing the heavy work. That is not aspirational anymore. That is just &lt;code&gt;npm create&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest limitations
&lt;/h2&gt;

&lt;p&gt;Three things I would rather not glaze over.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plugin ecosystems fragment.&lt;/strong&gt; Every Rust tool eventually faces the same fork: stay in Rust (fast, inaccessible to most JS contributors) or expose a JS plugin API (slower, ergonomic). Rolldown went hard on Rollup-plugin compatibility for adoption. Oxlint shipped a JS plugin alpha in March because too many ESLint rules people care about live in plugin land. Marvin Hagemeister's &lt;a href="https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-11/" rel="noopener noreferrer"&gt;Speeding up the JavaScript ecosystem&lt;/a&gt; series is the canonical write-up of this tension and worth reading if you are considering migrating.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The contributor bus factor is real.&lt;/strong&gt; Rust is a learning curve for JS-native maintainers. Concentration on Turbopack, Oxc, and Biome is worth pricing in if you are building on top of these tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory regressions happen.&lt;/strong&gt; Rolldown 1.0 RC.18 shipped with about 7x higher RSS than Vite 7 in dev for some apps before the fixes landed. Bundlers in Rust are not automatically more memory-efficient. They are faster, but the wins live in CPU time, not necessarily memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;

&lt;p&gt;If you have not migrated a project off ESLint yet, &lt;code&gt;bunx @biomejs/biome init&lt;/code&gt; plus &lt;code&gt;bun add -d oxlint&lt;/code&gt; will take you 15 minutes. The Biome migration commands (&lt;code&gt;biome migrate eslint&lt;/code&gt;, &lt;code&gt;biome migrate prettier&lt;/code&gt;) are doing the right thing in 2026. The Vite 8 upgrade is a &lt;code&gt;npm install vite@8&lt;/code&gt; for most apps. Next.js 16 is the same story; the Turbopack flag is just gone now.&lt;/p&gt;

&lt;p&gt;The faster builds are the part people notice. The smaller dependency tree is the part your security team should notice. The shared Rust core that could ship to mobile next quarter is the part your CTO should notice.&lt;/p&gt;

&lt;p&gt;It all started with one team being annoyed enough at webpack to write SWC. We got here by accident.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by &lt;a href="https://agnelnieves.com/about" rel="noopener noreferrer"&gt;Agnel Nieves&lt;/a&gt;, a design engineer with 15+ years across product, design systems, and crypto. More writing on &lt;a href="https://agnelnieves.com/blog" rel="noopener noreferrer"&gt;the blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>security</category>
    </item>
    <item>
      <title>Building a Terminal Portfolio You Can SSH Into</title>
      <dc:creator>Agnel Nieves</dc:creator>
      <pubDate>Tue, 12 May 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/agnelnieves/building-a-terminal-portfolio-you-can-ssh-into-3h9b</link>
      <guid>https://dev.to/agnelnieves/building-a-terminal-portfolio-you-can-ssh-into-3h9b</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhcfn5z48vqdpwoj796z1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhcfn5z48vqdpwoj796z1.png" alt="Screenshot of agnel's terminal with the ssh cli" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;You can now browse this site in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh agnelnieves.sh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No install. One Rust binary that runs as either a local CLI or an in-binary SSH server. No system sshd, no user accounts, no auth. The TUI is built with &lt;a href="https://ratatui.rs" rel="noopener noreferrer"&gt;ratatui&lt;/a&gt;; the SSH layer is &lt;a href="https://docs.rs/russh" rel="noopener noreferrer"&gt;russh&lt;/a&gt;; render code is shared between local and remote modes. It's hosted on Fly.io behind a dedicated IPv4, and the whole thing costs about $2/mo. The code lives as a &lt;code&gt;cli/&lt;/code&gt; package inside the same Next.js project that powers this site, so the website and the terminal share a single deploy story. If you want the full build (architecture, code, Fly.io setup), I wrote it up as a &lt;a href="https://agnelnieves.com/guides/ssh-terminal-portfolio.md" rel="noopener noreferrer"&gt;step-by-step guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ssh agnelnieves.sh&lt;/code&gt;. That's it. No &lt;code&gt;-l user@&lt;/code&gt;, no keys to add, no first-time signup. The server accepts whatever username your local SSH client offers and drops you straight into a terminal UI.&lt;/p&gt;

&lt;p&gt;Press &lt;code&gt;h&lt;/code&gt;/&lt;code&gt;a&lt;/code&gt;/&lt;code&gt;b&lt;/code&gt;/&lt;code&gt;c&lt;/code&gt; to jump between Home, About, Blog, and Connect. &lt;code&gt;j&lt;/code&gt;/&lt;code&gt;k&lt;/code&gt; (or arrows) to navigate lists. &lt;code&gt;Enter&lt;/code&gt; to open. &lt;code&gt;q&lt;/code&gt; to quit, which closes the SSH session.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I built it
&lt;/h2&gt;

&lt;p&gt;Honestly? I saw &lt;a href="https://terminal.shop" rel="noopener noreferrer"&gt;terminal.shop&lt;/a&gt; and thought it was awesome. They sell coffee at &lt;code&gt;ssh terminal.shop&lt;/code&gt; (that's the whole storefront, no website) and the first time I tried it I wanted to know if I could build something like it.&lt;/p&gt;

&lt;p&gt;I do side projects like this when I need to step away from whatever I'm shipping day-to-day. They're not pitches. They don't need a thesis. They're more like the way some people fix a bike on a Saturday: sit down, follow whatever's pulling on your curiosity, and at the end you have a thing that didn't exist before. That's the whole reason.&lt;/p&gt;

&lt;p&gt;So this was that. A few evenings poking at &lt;a href="https://ratatui.rs" rel="noopener noreferrer"&gt;ratatui&lt;/a&gt; and &lt;a href="https://docs.rs/russh" rel="noopener noreferrer"&gt;russh&lt;/a&gt; to see what it would take to make &lt;code&gt;ssh agnelnieves.sh&lt;/code&gt; actually work. Turns out: less than I expected. The hardest parts were the ones I didn't see coming, which is what makes any of this fun.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;p&gt;One binary. Two modes. One render path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            ┌─────────────────────────────┐
            │ agnel (single .exe) │
            └──────────────┬──────────────┘
                           │
            ┌──────────────┴──────────────┐
            │ │
       agnel (no flag) agnel --serve
       Local TUI SSH server (Fly.io)
       crossterm stdin russh + custom backend
            │ │
            └──────────┬──────────────────┘
                       │
                src/render.rs ← shared
                       │
                Per-session App state
                       │
                Live fetches from agnelnieves.com APIs

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pieces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TUI:&lt;/strong&gt; &lt;a href="https://ratatui.rs" rel="noopener noreferrer"&gt;ratatui&lt;/a&gt; draws everything: header, ticker, ASCII banner, projects list, blog reader. The render function lives in &lt;code&gt;src/render.rs&lt;/code&gt; and is identical whether the binary is running on your laptop or whether you're seeing it over SSH.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State machine:&lt;/strong&gt; &lt;code&gt;App&lt;/code&gt; in &lt;code&gt;src/app.rs&lt;/code&gt; owns screen, scroll position, list selection, ticker animation, and async-fetch results funneled through an &lt;code&gt;mpsc::Receiver&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data:&lt;/strong&gt; zero content is bundled. The CLI fetches live from &lt;code&gt;/feed.json&lt;/code&gt;, &lt;code&gt;/api/blog/[slug]/raw&lt;/code&gt;, &lt;code&gt;/api/projects&lt;/code&gt;, and &lt;code&gt;/api/site.json&lt;/code&gt; on this site, cached for an hour at &lt;code&gt;~/.agnel/cache/&lt;/code&gt;. So a new blog post here shows up in your terminal immediately without redeploying anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH server:&lt;/strong&gt; &lt;a href="https://docs.rs/russh" rel="noopener noreferrer"&gt;russh 0.60&lt;/a&gt; in &lt;code&gt;src/serve.rs&lt;/code&gt;. One tokio task per session.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The decision that made it easy: don't fork a PTY
&lt;/h2&gt;

&lt;p&gt;Most "expose a TUI over SSH" guides suggest spawning a pseudo-terminal and &lt;code&gt;exec&lt;/code&gt;-ing the TUI binary inside it. That's how &lt;code&gt;mosh&lt;/code&gt;, &lt;code&gt;tmux&lt;/code&gt;, and Go's &lt;a href="https://charm.sh/blog/wish/" rel="noopener noreferrer"&gt;&lt;code&gt;wish&lt;/code&gt;&lt;/a&gt; library (the one terminal.shop uses) handle things. It works, but it's heavier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds a libc dependency (&lt;code&gt;forkpty&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Forks a process per connection (more memory, slower spawn)&lt;/li&gt;
&lt;li&gt;Hard to share state between the SSH layer and the app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Russh's &lt;code&gt;ratatui_app&lt;/code&gt; example showed a cleaner path: implement a custom &lt;code&gt;std::io::Write&lt;/code&gt; backend that funnels bytes through russh's &lt;code&gt;Handle::data(channel, bytes)&lt;/code&gt; API. Ratatui doesn't know it's writing to an SSH channel instead of stdout. No fork, no PTY, just bytes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;TerminalSink&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UnboundedSender&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;TerminalSink&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.sink&lt;/span&gt;&lt;span class="nf"&gt;.extend_from_slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.sink&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.sender&lt;/span&gt;&lt;span class="nf"&gt;.send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;mem&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.sink&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A background tokio task drains the channel and pushes bytes back to the client through russh. Each session gets its own &lt;code&gt;App&lt;/code&gt; and its own &lt;code&gt;Terminal&amp;lt;CrosstermBackend&amp;lt;TerminalSink&amp;gt;&amp;gt;&lt;/code&gt;. The lock graph stays tiny: an &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;SessionState&amp;gt;&amp;gt;&lt;/code&gt; per session, plus a &lt;code&gt;HashMap&lt;/code&gt; of all sessions for cleanup.&lt;/p&gt;

&lt;p&gt;Inputs go the other direction. Russh hands raw bytes to my &lt;code&gt;data()&lt;/code&gt; callback, and a small parser turns them into the same &lt;code&gt;crossterm::event::KeyEvent&lt;/code&gt; values the local TUI uses. That meant zero changes to &lt;code&gt;App::handle_key&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;parse_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;KeyEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 0x03 → Ctrl+C, 0x1b[A → Up, 0x1b alone → Esc,&lt;/span&gt;
    &lt;span class="c1"&gt;// printable ASCII → Char(b), etc.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Window resize hooks into the same place. &lt;code&gt;window_change_request&lt;/code&gt; calls &lt;code&gt;terminal.resize(rect)&lt;/code&gt;, and ratatui redraws on the next 50 ms tick.&lt;/p&gt;

&lt;p&gt;The result: a single binary you can drop on a VPS, in a container, or even into a Lambda-style worker, and it just serves the TUI.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned about hosting
&lt;/h2&gt;

&lt;p&gt;A few things I didn't expect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Fly trial orgs block dedicated IPv4, and SSH needs IPv4.&lt;/strong&gt; For HTTP, Fly's edge gives you a shared anycast IPv4 for free. For raw TCP on port 22, you need a dedicated v4 (~$2/mo), and trial orgs can't allocate one. Pure-IPv6 isn't a real option, either: most US residential ISPs still don't have native IPv6 SSH paths. Adding a card was the unlock.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The SSH host key has to persist.&lt;/strong&gt; If the container generates a fresh Ed25519 key on every boot, every returning visitor gets &lt;code&gt;REMOTE HOST IDENTIFICATION HAS CHANGED&lt;/code&gt; warnings after the next deploy. The fix is a 1 GB Fly volume mounted at &lt;code&gt;/data&lt;/code&gt;, with &lt;code&gt;--host-key /data/ssh_host_key&lt;/code&gt;. First boot generates and writes the key; every boot after just reads it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Trial machines auto-stop after 5 minutes.&lt;/strong&gt; That's how the Fly free tier nudges you toward billing. Once you're paying, set &lt;code&gt;auto_stop_machines = "off"&lt;/code&gt; and &lt;code&gt;min_machines_running = 1&lt;/code&gt; if you want a long-running service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. &lt;code&gt;fly proxy&lt;/code&gt; is fine for HTTP, weird for raw SSH.&lt;/strong&gt; I burned half an hour on &lt;code&gt;Connection reset by peer&lt;/code&gt; through &lt;code&gt;fly proxy 12222:2222&lt;/code&gt; before realizing I should just &lt;code&gt;bash /dev/tcp/127.0.0.1/2222&lt;/code&gt; from inside the machine via &lt;code&gt;flyctl ssh console&lt;/code&gt;. The SSH server was healthy the whole time; the proxy path was the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  ANSI Shadow beats slant
&lt;/h2&gt;

&lt;p&gt;The first banner I shipped used figlet's &lt;code&gt;slant&lt;/code&gt; font. It looks fine in a docs file. At terminal width with full-block rendering, it falls apart into disconnected diagonals.&lt;/p&gt;

&lt;p&gt;Swapped to &lt;a href="https://patorjk.com/software/taag/#p=display&amp;amp;f=ANSI%20Shadow&amp;amp;t=AGNEL%20NIEVES" rel="noopener noreferrer"&gt;ANSI Shadow&lt;/a&gt;, the same boxy block font terminal.shop and &lt;a href="https://charm.sh" rel="noopener noreferrer"&gt;charm.sh&lt;/a&gt; use. Six rows tall, about 93 columns wide, reads instantly. Probably the kind of thing I'd have gotten right the first time if I'd looked at more references before opening figlet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visitor #N
&lt;/h2&gt;

&lt;p&gt;One fun thing I added. Look at the bottom-left of the footer when you connect: it says &lt;code&gt;visitor #N&lt;/code&gt;. First SSH session ever was #1, second was #2, and so on. The count lives in a tiny text file on the Fly volume next to the host key, increments once per &lt;code&gt;channel_open_session&lt;/code&gt;, and persists across deploys (host-key lesson applied).&lt;/p&gt;

&lt;p&gt;Implementation is about thirty lines. An &lt;code&gt;AtomicU64&lt;/code&gt; loaded from the file at startup, a &lt;code&gt;spawn_blocking&lt;/code&gt; write after each &lt;code&gt;fetch_add&lt;/code&gt;. The file is just a base-10 number with no header, so &lt;code&gt;cat /data/visitor_count&lt;/code&gt; is the dashboard.&lt;/p&gt;

&lt;p&gt;There's no leaderboard. No real meaning to the number. But watching it tick up after shipping is the moment the whole side project starts feeling worth it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Tech&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TUI&lt;/td&gt;
&lt;td&gt;ratatui 0.29, crossterm 0.28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH&lt;/td&gt;
&lt;td&gt;russh 0.60&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP client&lt;/td&gt;
&lt;td&gt;ureq 2 (sync, plenty for this)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async runtime&lt;/td&gt;
&lt;td&gt;tokio 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLI args&lt;/td&gt;
&lt;td&gt;clap 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Host&lt;/td&gt;
&lt;td&gt;Fly.io (&lt;code&gt;iad&lt;/code&gt;, shared-cpu-1x, 256 MB, 1 GB volume)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNS&lt;/td&gt;
&lt;td&gt;Vercel Domains (A + AAAA on the apex)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Binary size&lt;/td&gt;
&lt;td&gt;~3 MB release (macOS arm64), 4.3 MB (Linux x86_64)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image size&lt;/td&gt;
&lt;td&gt;28 MB (Debian bookworm-slim runtime)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;$2/mo for the dedicated IPv4 + free tier for the rest&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pretty errors and loading states.&lt;/strong&gt; Today they say &lt;code&gt;Loading…&lt;/code&gt; which is functional but boring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project deep-links.&lt;/strong&gt; I render project metadata but not the original write-ups. Pulling MDX through the API would let me ship long-form case studies in-terminal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-platform release binaries.&lt;/strong&gt; The current install path is &lt;code&gt;cargo install --path cli/&lt;/code&gt;. Homebrew tap + release artifacts for macOS and Linux is a Saturday morning.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh agnelnieves.sh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to build your own version, the &lt;a href="https://agnelnieves.com/guides/ssh-terminal-portfolio.md" rel="noopener noreferrer"&gt;guide&lt;/a&gt; has the full setup (Rust + ratatui + russh + Fly.io), with a copy-paste prompt at the top so you can hand it to your AI agent of choice. If you ship one, &lt;a href="https://agnelnieves.com/follow" rel="noopener noreferrer"&gt;let me know&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by &lt;a href="https://agnelnieves.com/about" rel="noopener noreferrer"&gt;Agnel Nieves&lt;/a&gt;, a design engineer with 15+ years across product, design systems, and crypto. More writing on &lt;a href="https://agnelnieves.com/blog" rel="noopener noreferrer"&gt;the blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>ratatui</category>
      <category>ssh</category>
      <category>flyio</category>
    </item>
    <item>
      <title>Optimizing Your Website for AI Agents and LLMs</title>
      <dc:creator>Agnel Nieves</dc:creator>
      <pubDate>Tue, 14 Apr 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/agnelnieves/optimizing-your-website-for-ai-agents-and-llms-1183</link>
      <guid>https://dev.to/agnelnieves/optimizing-your-website-for-ai-agents-and-llms-1183</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fll32v2kt40lfm96gnqv0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fll32v2kt40lfm96gnqv0.png" alt=" " width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your website has two audiences now. Humans, obviously. But also AI agents — LLMs that crawl, summarize, cite, and recommend your content to millions of people. If your site isn't optimized for both, you're leaving visibility on the table.&lt;/p&gt;

&lt;p&gt;I just finished optimizing &lt;a href="https://agnelnieves.com" rel="noopener noreferrer"&gt;my personal site&lt;/a&gt; for AI consumption, and the process revealed something interesting: most of what makes a site good for AI also makes it better for humans. Clear structure, machine-readable content, and explicit metadata benefit everyone.&lt;/p&gt;

&lt;p&gt;Here's what I did and why it matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are AI Agents Actually Doing with Your Site?
&lt;/h2&gt;

&lt;p&gt;When someone asks ChatGPT, Claude, Perplexity, or Google's AI Overview a question, those systems don't just generate answers from training data. Increasingly, they fetch and cite live web content. Your site might get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Crawled for training data&lt;/strong&gt; by bots like GPTBot, ClaudeBot, and Google-Extended&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fetched at query time&lt;/strong&gt; by Perplexity, ChatGPT browsing, and similar agents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cited as a source&lt;/strong&gt; in AI-generated responses&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Summarized in featured snippets&lt;/strong&gt; and AI overviews&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Navigated by autonomous agents&lt;/strong&gt; that interact with your APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these has different needs, but they all benefit from the same foundation: structured, discoverable, machine-readable content.&lt;/p&gt;

&lt;h2&gt;
  
  
  The llms.txt Standard
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://llmstxt.org" rel="noopener noreferrer"&gt;llms.txt spec&lt;/a&gt; is the equivalent of &lt;code&gt;robots.txt&lt;/code&gt; for AI agents. While &lt;code&gt;robots.txt&lt;/code&gt; tells crawlers what they &lt;em&gt;can&lt;/em&gt; access, &lt;code&gt;llms.txt&lt;/code&gt; tells them what your site &lt;em&gt;is&lt;/em&gt; — a structured markdown index served at your domain root.&lt;/p&gt;

&lt;p&gt;The format is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Your Name or Site&lt;/span&gt;
&lt;span class="gt"&gt;
&amp;gt; A one-line summary of what this site is.&lt;/span&gt;

A longer description paragraph.

&lt;span class="gu"&gt;## Section Name&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Link Title&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;: Description of what's at this link

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I implemented two variants:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/llms.txt&lt;/code&gt;&lt;/strong&gt; — the index. A table of contents with links to all pages, blog posts, projects, social profiles, and feeds. Think of it as a menu for AI agents to browse selectively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/llms-full.txt&lt;/code&gt;&lt;/strong&gt; — the full dump. Every blog post's complete markdown content, every project description, biographical context. For agents that want to load everything into context at once.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are served as &lt;code&gt;text/plain&lt;/code&gt; with markdown formatting. Both are generated dynamically from the same data sources that power the site, so they never go stale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inline LLM Instructions in HTML
&lt;/h2&gt;

&lt;p&gt;This one comes from a &lt;a href="https://vercel.com/blog/a-proposal-for-inline-llm-instructions-in-html" rel="noopener noreferrer"&gt;Vercel proposal&lt;/a&gt; and it's clever: embed AI-readable instructions directly in your page's &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; using a script tag browsers ignore.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/llms.txt"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Your&lt;/span&gt; &lt;span class="nx"&gt;Site&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt;

&lt;span class="nx"&gt;This&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;personal&lt;/span&gt; &lt;span class="nx"&gt;website&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="nx"&gt;based&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;

&lt;span class="err"&gt;##&lt;/span&gt; &lt;span class="nx"&gt;Site&lt;/span&gt; &lt;span class="nx"&gt;Structure&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="sr"&gt;/ — Home: Descriptio&lt;/span&gt;&lt;span class="err"&gt;n
&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="sr"&gt;/blog — Blog: Descriptio&lt;/span&gt;&lt;span class="err"&gt;n
&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="sr"&gt;/about — About: Descriptio&lt;/span&gt;&lt;span class="err"&gt;n
&lt;/span&gt;
&lt;span class="err"&gt;##&lt;/span&gt; &lt;span class="nx"&gt;Key&lt;/span&gt; &lt;span class="nx"&gt;Facts&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Your&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Your&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;Specialties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Thing&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Thing&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Thing&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browsers skip &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags with unknown types. LLMs process them. It's a zero-cost way to give every page on your site a machine-readable context block. I added one to my root layout that describes who I am, the site structure, and where to find machine-readable content.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structured Data That AI Engines Actually Use
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://json-ld.org/" rel="noopener noreferrer"&gt;JSON-LD&lt;/a&gt; structured data has always been important for Google. It's now equally important for AI engines. When an LLM encounters schema.org markup, it understands the &lt;em&gt;semantics&lt;/em&gt; of your content — not just the text, but what the text represents.&lt;/p&gt;

&lt;p&gt;I already had structured data for my blog posts (&lt;code&gt;BlogPosting&lt;/code&gt; schema with breadcrumbs). What I added was &lt;code&gt;CreativeWork&lt;/code&gt; schema for my &lt;a href="https://agnelnieves.com/work" rel="noopener noreferrer"&gt;portfolio projects&lt;/a&gt;, giving each project a machine-readable identity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://schema.org"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CreativeWork"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Project Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"What this project is"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://project-url.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"creator"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Person"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your Name"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The more schema types you cover, the more AI engines can understand and cite your work with proper attribution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Machine-Readable Feeds
&lt;/h2&gt;

&lt;p&gt;RSS is great, but it's XML — not the most natural format for AI agents to parse. I added a &lt;a href="https://www.jsonfeed.org/" rel="noopener noreferrer"&gt;JSON Feed&lt;/a&gt; endpoint alongside my existing RSS feed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/feed.xml&lt;/code&gt;&lt;/strong&gt; — RSS 2.0 for traditional feed readers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/feed.json&lt;/code&gt;&lt;/strong&gt; — JSON Feed 1.1 for programmatic consumption&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;JSON Feed is cleaner for AI agents to parse and reference. Both are registered in the site's metadata so they're auto-discoverable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making robots.txt AI-Aware
&lt;/h2&gt;

&lt;p&gt;Most sites already have a &lt;code&gt;robots.txt&lt;/code&gt;. The key addition is explicitly allowing AI crawlers and pointing them to your &lt;code&gt;llms.txt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;GPTBot&lt;/span&gt;
&lt;span class="n"&gt;Allow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;ClaudeBot&lt;/span&gt;
&lt;span class="n"&gt;Allow&lt;/span&gt;: /

&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: &lt;span class="n"&gt;PerplexityBot&lt;/span&gt;
&lt;span class="n"&gt;Allow&lt;/span&gt;: /

&lt;span class="n"&gt;Sitemap&lt;/span&gt;: &lt;span class="n"&gt;https&lt;/span&gt;://&lt;span class="n"&gt;yoursite&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;/&lt;span class="n"&gt;sitemap&lt;/span&gt;.&lt;span class="n"&gt;xml&lt;/span&gt;

&lt;span class="c"&gt;# AI/LLM Content
# llms.txt: https://yoursite.com/llms.txt
# llms-full.txt: https://yoursite.com/llms-full.txt
&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Many sites block AI crawlers by default. If you &lt;em&gt;want&lt;/em&gt; your content cited and discovered by AI, explicitly allow the major bots: &lt;code&gt;GPTBot&lt;/code&gt;, &lt;code&gt;ChatGPT-User&lt;/code&gt;, &lt;code&gt;Google-Extended&lt;/code&gt;, &lt;code&gt;ClaudeBot&lt;/code&gt;, &lt;code&gt;anthropic-ai&lt;/code&gt;, &lt;code&gt;PerplexityBot&lt;/code&gt;, &lt;code&gt;Applebot-Extended&lt;/code&gt;, &lt;code&gt;Bytespider&lt;/code&gt;, and &lt;code&gt;cohere-ai&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters for Creators
&lt;/h2&gt;

&lt;p&gt;As a design engineer with 15+ years of building products, I've watched SEO evolve from keyword stuffing to semantic web to AI-native discovery. We're at an inflection point. The sites that get cited by AI aren't necessarily the ones with the best domain authority — they're the ones with the clearest, most structured, most machine-readable content.&lt;/p&gt;

&lt;p&gt;This is especially important for personal sites and portfolios. When someone asks an AI "who are the best design engineers in Miami?" or "what's a good article about design tokens?", you want your site to be citable. That requires more than good content — it requires content that AI can &lt;em&gt;find&lt;/em&gt;, &lt;em&gt;understand&lt;/em&gt;, and &lt;em&gt;attribute&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Full Stack of AI Optimization
&lt;/h2&gt;

&lt;p&gt;Here's the complete checklist of what I now have in place:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;robots.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Explicitly allow AI bots&lt;/td&gt;
&lt;td&gt;Let them crawl&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sitemap.xml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dynamic sitemap with all content&lt;/td&gt;
&lt;td&gt;Let them discover&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;llms.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Markdown index of the site&lt;/td&gt;
&lt;td&gt;Let them understand structure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;llms-full.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full content in one file&lt;/td&gt;
&lt;td&gt;Let them ingest everything&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inline &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Page-level LLM instructions&lt;/td&gt;
&lt;td&gt;Let them understand context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON-LD&lt;/td&gt;
&lt;td&gt;Structured data on every page&lt;/td&gt;
&lt;td&gt;Let them understand semantics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RSS + JSON Feed&lt;/td&gt;
&lt;td&gt;Machine-readable content feeds&lt;/td&gt;
&lt;td&gt;Let them subscribe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Meta tags&lt;/td&gt;
&lt;td&gt;OpenGraph, Twitter, canonical&lt;/td&gt;
&lt;td&gt;Let them cite accurately&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;None of these changes affect how the site looks or feels for human visitors. They're invisible additions that make the site dramatically more useful for AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;The AI web is evolving fast. Standards like &lt;code&gt;llms.txt&lt;/code&gt; are still emerging, and new patterns will appear. But the fundamentals won't change: structure your content clearly, make it discoverable, and give machines the metadata they need to understand it.&lt;/p&gt;

&lt;p&gt;If you want to replicate this setup, I've published a &lt;a href="https://agnelnieves.com/guides/ai-optimization-guide.md" rel="noopener noreferrer"&gt;full implementation guide&lt;/a&gt; with code examples for Next.js. The approach works for any framework — the concepts are universal.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Building something and want to talk AI optimization? &lt;a href="https://agnelnieves.com/follow" rel="noopener noreferrer"&gt;Let's connect&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>seo</category>
      <category>webdev</category>
      <category>llms</category>
    </item>
    <item>
      <title>We Found a Hidden Pet System in Claude Code's Leaked Source and Shipped It Overnight</title>
      <dc:creator>Agnel Nieves</dc:creator>
      <pubDate>Wed, 01 Apr 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/agnelnieves/we-found-a-hidden-pet-system-in-claude-codes-leaked-source-and-shipped-it-overnight-23fk</link>
      <guid>https://dev.to/agnelnieves/we-found-a-hidden-pet-system-in-claude-codes-leaked-source-and-shipped-it-overnight-23fk</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsft85sz04tvf7vboxag8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsft85sz04tvf7vboxag8.png" alt="Screenshot of claudebuddy" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On March 30th, 2026, Anthropic accidentally shipped a 59.8 MB source map file inside their Claude Code npm package. Within hours, the entire 512,000-line TypeScript codebase was mirrored across GitHub and picked apart by thousands of developers. Buried inside that code, alongside feature flags for autonomous agents and undercover commit modes, was something nobody expected: a fully built virtual pet system called &lt;strong&gt;Buddy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;My co-founder &lt;a href="https://x.com/peronif5" rel="noopener noreferrer"&gt;peroni&lt;/a&gt; and I did the only sensible thing. We shipped it.&lt;/p&gt;



&lt;blockquote&gt;
&lt;p&gt;We all know what happened yday with Claude Code. Buried in the source: a hidden pet system called "Buddy." Every user gets a unique pixel creature based on their ID. Deterministic, same hash, same buddy, every time. So &lt;a href="https://twitter.com/peronif5?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;@peronif5&lt;/a&gt; and I did the most sensible thing... Shipped it.… &lt;a href="https://t.co/YE4ZSEIXq7" rel="noopener noreferrer"&gt;pic.twitter.com/YE4ZSEIXq7&lt;/a&gt;&lt;/p&gt;— Agnel (🇵🇷) (&lt;a class="mentioned-user" href="https://dev.to/agnelnieves"&gt;@agnelnieves&lt;/a&gt;) &lt;a href="https://twitter.com/agnelnieves/status/2039311800005525807?ref_src=twsrc%5Etfw" rel="noopener noreferrer"&gt;April 1, 2026&lt;/a&gt;
&lt;/blockquote&gt;



&lt;h2&gt;
  
  
  What Happened with the Claude Code Leak
&lt;/h2&gt;

&lt;p&gt;If you missed it, here's the short version. Chaofan Shou (&lt;a href="https://x.com/Fried_rice" rel="noopener noreferrer"&gt;@Fried_rice&lt;/a&gt;) noticed that version 2.1.88 of the &lt;code&gt;@anthropic-ai/claude-code&lt;/code&gt; package on npm included an unminified source map — &lt;code&gt;cli.js.map&lt;/code&gt; — containing the full, readable TypeScript source. By 4:23 AM ET, it was public. By noon, the internet had catalogued every hidden feature, internal codename, and unreleased capability Anthropic had been quietly building.&lt;/p&gt;

&lt;p&gt;Among the bigger discoveries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;KAIROS&lt;/strong&gt; — an always-on daemon mode that lets Claude Code operate as a persistent background agent, watching, logging, and acting without waiting for user input&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Undercover mode&lt;/strong&gt; — auto-activated for Anthropic employees on public repos, stripping AI attribution from commits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;44 feature flags&lt;/strong&gt; covering unreleased functionality&lt;/li&gt;
&lt;li&gt;And tucked away, a complete companion pet system called &lt;strong&gt;Buddy&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Buddy System: A Pixel Pet for Every User
&lt;/h2&gt;

&lt;p&gt;The Buddy system was fully implemented. Every Claude Code user was supposed to get a unique pixel creature generated deterministically from their user ID. Same hash, same buddy, every time. The code included 18 species across rarity tiers, stat generation, personality descriptions — the whole gacha experience, just waiting to be turned on.&lt;/p&gt;

&lt;p&gt;It was the kind of detail that makes you smile. In a tool built for productivity and code generation, someone at Anthropic took the time to build a pet system. A little pixel friend that lives in your terminal. That's the kind of craft and whimsy that makes developer tools memorable.&lt;/p&gt;

&lt;p&gt;The moment I saw it, I knew what we had to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Discovery to Deploy in a Day
&lt;/h2&gt;

&lt;p&gt;Peroni and I have a rhythm. We spot something interesting, we build. No planning committee, no Jira tickets, no "let's circle back Monday." As a design engineer with 15+ years of shipping products, I've learned that the best side projects are the ones you can't &lt;em&gt;not&lt;/em&gt; build. This was one of those.&lt;/p&gt;

&lt;p&gt;We built &lt;a href="https://www.claudebuddy.me/" rel="noopener noreferrer"&gt;Claude Buddy&lt;/a&gt; — a web app that lets anyone generate their own pixel companion. Type your name or your Claude Code user ID, and watch it draw your buddy pixel by pixel, complete with retro CRT animations and pop sounds.&lt;/p&gt;

&lt;p&gt;Here's what we shipped:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;12 unique pixel art species&lt;/strong&gt; across 5 rarity tiers — from common Blobbits to the legendary Nebulynx (3% chance)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic generation&lt;/strong&gt; — same name always produces the same buddy, making them feel like &lt;em&gt;yours&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shiny variants&lt;/strong&gt; with a 5% drop rate, because of course&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Buddy stats&lt;/strong&gt; — Vibe, Chaos, Focus, and Luck, each randomly rolled but consistent to your hash&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One-command terminal install&lt;/strong&gt; — &lt;code&gt;curl&lt;/code&gt; a script and your buddy appears in your Claude Code statusline&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Social sharing&lt;/strong&gt; — download as PNG, share via URL, generate QR codes, post to X/LinkedIn with pre-populated text&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole thing runs on Next.js 15, renders sprites on HTML5 Canvas, and uses a Mulberry32 PRNG seeded by a DJB2 hash of your input. No backend, no database, no authentication. Pure deterministic fun.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Build This?
&lt;/h2&gt;

&lt;p&gt;Partly because it's fun. Partly because of &lt;a href="https://dev.to/agnelnieves/the-ai-native-design-gap-from-static-to-dynamic-experiences-2mb4"&gt;how I approach creative work&lt;/a&gt; — I try to ship a hackathon-style project every quarter to stay sharp and experiment with new patterns. But mostly because I think the Buddy system represents something important about how we relate to our tools.&lt;/p&gt;

&lt;p&gt;Developer tools don't have to be purely utilitarian. The best ones have personality. They reward curiosity. They make you &lt;em&gt;want&lt;/em&gt; to open the terminal. A pixel pet that lives next to your cursor won't make you a better programmer, but it might make the work feel a little less solitary.&lt;/p&gt;

&lt;p&gt;Anthropic clearly felt the same way — they built the whole thing, ready to ship. We just opened the door a little early.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Generation Algorithm Works
&lt;/h2&gt;

&lt;p&gt;For the technically curious, the generation is straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Take the input string (name or user ID), lowercase and trim it&lt;/li&gt;
&lt;li&gt;Salt it with a fixed string to avoid collisions&lt;/li&gt;
&lt;li&gt;Run a DJB2 hash to convert it to a numeric seed&lt;/li&gt;
&lt;li&gt;Feed that seed into a Mulberry32 PRNG&lt;/li&gt;
&lt;li&gt;Roll for species (weighted by rarity tier probabilities)&lt;/li&gt;
&lt;li&gt;Roll for shiny status (5% chance)&lt;/li&gt;
&lt;li&gt;Generate four stats between 1 and 99&lt;/li&gt;
&lt;li&gt;Select a soul description from the species pool&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Same input, same seed, same rolls, same buddy. Every time. No server involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's Open Source
&lt;/h2&gt;

&lt;p&gt;The entire project is &lt;a href="https://github.com/basement-browser/claude-buddy" rel="noopener noreferrer"&gt;open source on GitHub&lt;/a&gt;. Built by the &lt;a href="https://basementbrowser.com" rel="noopener noreferrer"&gt;Basement&lt;/a&gt; team. Fork it, remix it, hatch your own buddy.&lt;/p&gt;

&lt;p&gt;If you want to see what came out of this, check the &lt;a href="https://x.com/agnelnieves/status/2039311800005525807" rel="noopener noreferrer"&gt;thread on X&lt;/a&gt; where we announced it. And if you're building with Claude Code, maybe your buddy is already waiting — just type your name and find out.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have thoughts on this or want to collaborate on something weird? &lt;a href="https://dev.to/connect"&gt;Let's connect&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claudecode</category>
      <category>opensource</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>The AI-Native Design Gap: From Static to Dynamic Experiences</title>
      <dc:creator>Agnel Nieves</dc:creator>
      <pubDate>Thu, 16 Oct 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/agnelnieves/the-ai-native-design-gap-from-static-to-dynamic-experiences-2mb4</link>
      <guid>https://dev.to/agnelnieves/the-ai-native-design-gap-from-static-to-dynamic-experiences-2mb4</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fto3bqt7mdj6ozj9al3x8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fto3bqt7mdj6ozj9al3x8.png" alt="Abstract lines" width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's something I see all the time: teams spend weeks perfecting static mockups in Figma, present them with confidence, and then watch as stakeholders nod politely but don't quite get it. Why? Well, we're designing static artifacts for dynamic experiences.&lt;/p&gt;

&lt;p&gt;Every scroll, swipe, and tap in a real product triggers visual feedback. It's how users actually understand what's happening. Yet we keep pitching ideas as if they exist in freeze-frame.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI-Native Design Challenge
&lt;/h2&gt;

&lt;p&gt;This gap matters more than ever, especially if you're working on AI-native products. Whether it's OpenAI's ChatGPT, Perplexity's search interface, or Claude's conversations—these products are rewriting the rules of digital interaction. They're introducing entirely new patterns: streaming responses, conversational flows, adaptive interfaces that feel less like traditional apps and more like living dialogues. Even more so if the interactions include voice motion which is supposed to react to the user's voice.&lt;/p&gt;

&lt;p&gt;You can't capture that in a static frame. You have to show it in motion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Interaction Design Is an Unlocked Skill
&lt;/h2&gt;

&lt;p&gt;Here's the thing about motion and interaction design: it's a toolkit you unlock through experimentation, not strictly tied to theory or studies. You learn it by thinking about physics, natural movement, user intent—concepts you absorb from using dozens of different apps and noticing what feels right. Think about it, try to explain a motion ui pattern to someone, try to explain the core concept of what you have in your mind. It's difficult… It's open to interpretation of the person to whom you're explaining it to, and limited by their imagination (which is highly likely different from yours).&lt;/p&gt;

&lt;p&gt;This creates a knowledge gap. Most designers haven't had the chance to experiment enough with motion to build that intuition. But here's what I've learned: even designing interactions frame-by-frame, storyboard-style like old Disney animations, can be enough to transform how you communicate ideas. It forces you to think through every transition, every state change, every moment of feedback.&lt;/p&gt;

&lt;p&gt;And it doesn't just help you—it helps engineering understand exactly what you're trying to build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design Is Taking the Spotlight Again
&lt;/h2&gt;

&lt;p&gt;We're in an exciting moment. With AI tools and no-code platforms, pretty much anyone can spin up a functional prototype. The technical barrier to entry has lowered significantly.&lt;/p&gt;

&lt;p&gt;What does that mean? Design becomes the differentiator. When everyone can build something that works, the products that win are the ones that feel incredible to use. Motion, polish, and thoughtful interaction patterns aren't nice-to-haves anymore—they're what separates successful products from forgettable ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Approach: Build to Learn
&lt;/h2&gt;

&lt;p&gt;I try to ship a hackathon-style project every quarter. Not because I need more side projects, but because it's the only way to stay ahead and fresh. New patterns emerge constantly. AI products iterate at light speed. Existing products unveil interactions you never even considered.&lt;/p&gt;

&lt;p&gt;The only way to keep your intuition sharp is to experiment relentlessly with what's out there.&lt;/p&gt;

&lt;p&gt;This comes from personal experience. My background has helped me a lot in this area—I started as a designer, shifted to software engineering, and eventually came back to design. That engineering foundation changed everything. I can quickly prototype motion patterns directly in code (often faster than in design tools), play with them until something clicks, then bring those learnings back to Figma to polish everything together.&lt;/p&gt;

&lt;p&gt;This workflow might sound backwards, but to me, it makes sense and it just works: code/define the motion first, design and polish last. Code gives you the freedom to experiment rapidly. Spin up prototypes quickly, and gather feedback. Design tools give you the precision to perfect it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Practical Takeaway
&lt;/h2&gt;

&lt;p&gt;If you're trying to sell an idea—whether to stakeholders, users, or investors—don't stop at static screens. Invest time in showing how it moves. Show how it responds. Show how it feels.&lt;/p&gt;

&lt;p&gt;You don't need to be an engineer to do this. Start simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sketch interaction sequences frame-by-frame&lt;/li&gt;
&lt;li&gt;Use Figma's prototyping features to simulate key transitions&lt;/li&gt;
&lt;li&gt;Try tools like ProtoPie, Lottie Lab for more complex motion&lt;/li&gt;
&lt;li&gt;Or, if you're comfortable with code, prototype directly in React or HTML&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The medium matters less than the commitment: design for motion, not just for pixels.&lt;/p&gt;

&lt;p&gt;Because in an era where AI is democratizing creation, the products that win won't just work—they'll feel magical. And you can't explain magic in a static mockup.&lt;/p&gt;

</description>
      <category>design</category>
      <category>motion</category>
      <category>ai</category>
      <category>prototyping</category>
    </item>
  </channel>
</rss>
