<?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: Ikkun</title>
    <description>The latest articles on DEV Community by Ikkun (@ikkun1222).</description>
    <link>https://dev.to/ikkun1222</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4078352%2F0c81ce0e-3272-4e25-8763-0d733bb9806b.png</url>
      <title>DEV Community: Ikkun</title>
      <link>https://dev.to/ikkun1222</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ikkun1222"/>
    <language>en</language>
    <item>
      <title>Oracle Cloud free tier: the always-free ARM instance</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Fri, 06 Nov 2026 00:15:44 +0000</pubDate>
      <link>https://dev.to/ikkun1222/oracle-cloud-free-tier-the-always-free-arm-instance-37o</link>
      <guid>https://dev.to/ikkun1222/oracle-cloud-free-tier-the-always-free-arm-instance-37o</guid>
      <description>&lt;p&gt;Yes, Oracle Cloud's always-free tier really does include a permanently free ARM instance — I run a meaningful part of my agent infrastructure on one. The catch people miss is not the price, it is the availability and the mindset. The free-tier ARM instance hosts agent services and a long-running site in my stack, and it has been genuinely useful. But I treat it as a nearly-free compute resource with a recall clause, not as a permanent home for anything I cannot afford to lose.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the always-free tier gives you
&lt;/h2&gt;

&lt;p&gt;Oracle Cloud's free tier covers a small set of always-free resources, distinct from the temporary trial credit you start with. The part that stands out for people running agents is the ARM instance: it is always-free rather than a time-boxed trial, which means you can leave a service running on it indefinitely without it expiring. For an always-on workload — an agent service that needs to answer around the clock, or a site that is expected to stay up — that long-running nature is the whole point. A time-limited free trial that vanishes after a month is useless for a service; an always-free instance is a different category of thing entirely.&lt;/p&gt;

&lt;p&gt;I deliberately keep the platform's monitoring agents on it as well. Some installations strip those out to save resources or reduce noise, but I leave them in place. They are the console's eyes — the signal I rely on to notice when something on the instance stops behaving. On a free resource with no support contract to call, that visibility matters more than the modest overhead it costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I use it, and where I do not
&lt;/h2&gt;

&lt;p&gt;Generic terms here, because the specifics matter less than the lesson. The ARM instance runs agent services and a long-running site in my infrastructure. It handles sustained, always-on work that does not need the headroom of my paid hardware. That split is deliberate: free-tier compute is excellent for workloads that are steady and predictable, and awkward for anything that suddenly needs a burst of cycles.&lt;/p&gt;

&lt;p&gt;What I do not do is treat it as my only copy of anything. If a service on the free tier dies, I want the recovery to be a re-provision and a restore, not a reconstruction. That is the discipline the &lt;a href="https://agentjournal.dev/blog/free-tier-stack/" rel="noopener noreferrer"&gt;free-tier stack&lt;/a&gt; post lays out, and it applies here more than anywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  The caveat that changes the design
&lt;/h2&gt;

&lt;p&gt;The honest caveat: instance availability on the free tier can be reclaimed. Oracle can take an always-free instance back, and when it does, the instance is gone — you do not get a negotiation, you get a notice. I have not been bitten, but I design as if I will be. That constraint is what forces the important habit: keep state somewhere else.&lt;/p&gt;

&lt;p&gt;The principle is simple. The instance is a cattle resource, not a pet. Its job is to run the compute. The data it produces and the state its services depend on live elsewhere, backed up and recoverable. That way, if the instance is reclaimed, it is an inconvenience — a re-provision, a restore, a few hours of my time — not a disaster where I lose the accumulated state my agents depend on. My &lt;a href="https://agentjournal.dev/blog/agent-memory/" rel="noopener noreferrer"&gt;agent memory&lt;/a&gt; layer and its vector store live outside the free instance for exactly this reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mental model
&lt;/h2&gt;

&lt;p&gt;The always-free ARM instance is real, capable compute at zero monthly cost, and that makes it easy to over-trust it. The useful frame is: free compute, guaranteed only until it is not. Run the always-on services on it, keep the platform's monitoring eyes attached, and keep everything that matters recoverable somewhere that a reclaim cannot touch. Done that way, it is one of the best cost-per-mileage pieces of my whole stack. Done the other way, it is a single point of failure wrapped in a great price.&lt;/p&gt;

&lt;p&gt;What would you be willing to re-provision from scratch in exchange for a permanently free ARM instance?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://agentjournal.dev/blog/oracle-free-tier/" rel="noopener noreferrer"&gt;agentjournal.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>agents</category>
      <category>monitoring</category>
      <category>ai</category>
    </item>
    <item>
      <title>Alpha Vantage limits and the Japanese stock gap</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Wed, 04 Nov 2026 00:15:45 +0000</pubDate>
      <link>https://dev.to/ikkun1222/alpha-vantage-limits-and-the-japanese-stock-gap-4ll7</link>
      <guid>https://dev.to/ikkun1222/alpha-vantage-limits-and-the-japanese-stock-gap-4ll7</guid>
      <description>&lt;p&gt;When I first went to wire up a stock data feed, Alpha Vantage looked like the obvious default: it's free, it's a real API, and it covers a huge swath of assets. So I wrote my pipeline against it — and then discovered two things that changed how I think about any free financial data feed. First, the free tier is rate-limited so tightly (I measured roughly 5 requests per minute) that any loop needs a sleep between calls. Second, and bigger for me: it doesn't cover Japanese stocks at all, which is the market I actually needed. Checked and confirmed in my own setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  The free tier is a scheduling constraint, not a firehose
&lt;/h2&gt;

&lt;p&gt;The headline "free" hides a real operational constraint. On the free tier I observed roughly 5 requests per minute, which is fine if your job makes a handful of calls. But if you write a loop that iterates over a list of tickers without throttling, you'll blow through the limit in the first few seconds and spend the rest of the run backing off.&lt;/p&gt;

&lt;p&gt;My fix was boring and effective: a sleep between requests. I pace the loop to stay under the observed rate, and I treat the limit as part of the contract I'm designing against rather than a surprise. This is the same discipline I apply to any upstream that isn't mine to hammer — throttle, back off, and make the job idempotent so a partial run can resume cleanly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Alpha Vantage does cover well
&lt;/h2&gt;

&lt;p&gt;To be fair, the coverage where it exists is broad. The API handles US and other global stocks, forex, crypto, commodities, and economic indicators. If your universe is US equities, a free-tier Alpha Vantage feed with a throttled loop is a perfectly reasonable place to start a research pipeline. The rate limit is manageable when your request volume is small.&lt;/p&gt;

&lt;p&gt;That's exactly why the gap stung: the tool is good at the things it does, and the missing piece is precisely the market I needed. For Japanese equity data, Alpha Vantage simply wasn't the answer, and no amount of throttling fixes a coverage hole.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Japanese stock gap
&lt;/h2&gt;

&lt;p&gt;The hard lesson wasn't about rate limits — it was about coverage. Japanese stocks aren't on the free global feed, and that's a category problem rather than a config problem. You can't sleep your way to data the API doesn't have.&lt;/p&gt;

&lt;p&gt;So for Japanese tickers I use a Japan-specific source instead, one that covers daily OHLCV going back years plus valuation and dividend data. Different endpoint, different rate budget, same throttling discipline. The point is that the two feeds serve different asset classes, and choosing between them is a coverage decision first and a pricing decision second.&lt;/p&gt;

&lt;h2&gt;
  
  
  The decision rule I now run with
&lt;/h2&gt;

&lt;p&gt;Before I build any pipeline on a free financial feed, I check three things in order. Coverage: does it actually serve my market and asset class? Rate limits: can the free tier sustain my request volume once throttled, or will I be waiting all day? Officialness: is there an official source for this data that's more reliable and documented? Alpha Vantage fails me on the first question for Japan, which is enough to disqualify it for that job no matter how good the rest is — the same reasoning I applied when I replaced scraping with proper Japanese data feeds.&lt;/p&gt;

&lt;p&gt;The rule generalizes beyond stocks. Any free API with a tight free tier and partial coverage is a candidate for the "check before you build" list. Read the limits, confirm the coverage actually includes your universe, and only then write the pipeline.&lt;/p&gt;

&lt;p&gt;What's your go-to when a free API covers most of the world but not the one market you actually trade — do you tolerate the gap or switch sources entirely?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://agentjournal.dev/blog/alpha-vantage-limits/" rel="noopener noreferrer"&gt;agentjournal.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>data</category>
      <category>api</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Enterprise Agent Selection: A Field Report (Series)</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Mon, 02 Nov 2026 00:15:44 +0000</pubDate>
      <link>https://dev.to/ikkun1222/enterprise-agent-selection-a-field-report-series-527j</link>
      <guid>https://dev.to/ikkun1222/enterprise-agent-selection-a-field-report-series-527j</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: When an organization handles information that must not leak, audit coverage decides the agent platform — not model quality. This series walks through a real selection process I supported, why a desktop app excluded from Compliance API lost the lead role, and what would change the conclusion.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One-line summary&lt;/strong&gt;: Audit coverage &amp;gt; model benchmarks for regulated environments; pick the platform whose &lt;em&gt;entire&lt;/em&gt; surface set is auditable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most articles about choosing an AI platform are written by and for engineering organizations. This one is not. The organization I recently helped has no dedicated engineering staff, handles pre-release information whose exposure carries material consequences, and runs ordinary managed Windows laptops. If that sounds unusual: per 2026 surveys, only ~12% of companies have deployed AI agents at all (under half among large enterprises). The constraint set in this series is not exotic — it is the default starting point for most first-time adopters.&lt;/p&gt;

&lt;p&gt;I've anonymized everything the NDA covers. What remains is the decision process, which is the part worth recording.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this series exists
&lt;/h2&gt;

&lt;p&gt;The existing guide on this site, &lt;a href="https://agentjournal.dev/blog/choosing-your-agent/" rel="noopener noreferrer"&gt;Choosing Your Agent&lt;/a&gt;, answers "which agent should &lt;em&gt;I&lt;/em&gt; run." This series answers a harder question: &lt;strong&gt;"which platform can my organization live with for the length of a contract?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That reframing matters because three things are expensive to change after signing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Audit coverage&lt;/strong&gt; — what the vendor records about user activity, and whether you can pull it into your own systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Management plane shape&lt;/strong&gt; — how many consoles, toggles, and config files it takes to keep the deployment consistent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost structure&lt;/strong&gt; — where the bill is flat (seats) and where it is variable (usage).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Model quality, feature velocity, benchmark deltas — all of that churns monthly. The three items above are structural. You inherit them for the contract term.&lt;/p&gt;

&lt;h2&gt;
  
  
  The parts
&lt;/h2&gt;

&lt;p&gt;Each part stands alone: direct answer in the first paragraph, tables over prose, facts dated "as of August 2026," and a one-line takeaway you can lift into your own decision memo.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Title&lt;/th&gt;
&lt;th&gt;Core question&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;&lt;a href="https://agentjournal.dev/blog/audit-gap-decides-platform/" rel="noopener noreferrer"&gt;The Audit Gap Decides the Platform&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Which surfaces actually land in audit logs?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;One Admin Console Beats Three&lt;/td&gt;
&lt;td&gt;How much management complexity can a staff-less org absorb?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;What Seat Pricing Actually Covers&lt;/td&gt;
&lt;td&gt;Where does the flat rate end and usage billing begin?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Why Command Allowlists Fail Alone&lt;/td&gt;
&lt;td&gt;What actually constrains an agent on a managed laptop?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Research Tasks on a Locked-Down Network&lt;/td&gt;
&lt;td&gt;Can agents do web research under default-deny egress?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;The Decision, and What Would Change It&lt;/td&gt;
&lt;td&gt;What was chosen, and which facts would reverse it?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;(Links go live as each part publishes.)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Who this is for
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Compliance / IT owners at companies adopting AI agents &lt;strong&gt;without&lt;/strong&gt; a dedicated engineering team&lt;/li&gt;
&lt;li&gt;Vendors' docs readers who want to know which admin controls matter first&lt;/li&gt;
&lt;li&gt;Anyone who has been asked "can we just buy licenses?" by their board&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: Is this a recommendation to avoid Anthropic?&lt;/strong&gt;&lt;br&gt;
No. It's a record of one selection process where audit coverage outweighed model preference. The reversal conditions are documented in Part 6.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Are the prices in this series official?&lt;/strong&gt;&lt;br&gt;
All pricing is quoted as of August 2026 from public plan pages; Enterprise tiers are custom quotes, so treat numbers as directional.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Why write about a client engagement?&lt;/strong&gt;&lt;br&gt;
Because the failure mode — picking a platform nobody can audit or operate later — is common enough that the process deserves documentation.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Next: Part 1 — The Audit Gap Decides the Platform&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://agentjournal.dev/blog/enterprise-agent-selection/" rel="noopener noreferrer"&gt;agentjournal.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claude</category>
      <category>security</category>
      <category>agents</category>
      <category>ai</category>
    </item>
    <item>
      <title>Running AI Coding Agents in Production: My 2026 Stack</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Fri, 30 Oct 2026 00:15:38 +0000</pubDate>
      <link>https://dev.to/ikkun1222/running-ai-coding-agents-in-production-my-2026-stack-4fh1</link>
      <guid>https://dev.to/ikkun1222/running-ai-coding-agents-in-production-my-2026-stack-4fh1</guid>
      <description>&lt;p&gt;Everyone has an opinion about AI agents. Almost nobody publishes their actual production setup — the gateways, the &lt;a href="https://agentjournal.dev/blog/mcp-server-security/" rel="noopener noreferrer"&gt;MCP servers&lt;/a&gt;, the credential handling, the monthly bill. This is mine, as of August 2026, with real numbers and the failures included.&lt;/p&gt;

&lt;p&gt;I run coding and automation agents daily for real work: this site, open-source tooling, and the automation that runs my infrastructure. Here's the stack, layer by layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The agents (division of labor)
&lt;/h2&gt;

&lt;p&gt;I don't use one agent for everything. Using the right tool per task is the cheapest optimization available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;opencode&lt;/strong&gt; — my open-source coding agent for repository-scale work. Runs with a local provider config, executes code, verifies results. It's the workhorse for feature work where I want full control of the loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code / Codex&lt;/strong&gt; — used for sessions where their specific strengths apply (long-context refactors, or tasks tuned to their model families). I treat them as specialists, not a default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hermes (Nous Research)&lt;/strong&gt; — the automation layer. &lt;a href="https://agentjournal.dev/blog/cron-job-monitoring/" rel="noopener noreferrer"&gt;Cron jobs&lt;/a&gt;, scheduled pipelines, monitoring: things that run while I sleep and message me when they break. This is the agent that writes the drafts for this site, runs my infrastructure checks, and handles repetitive ops.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rule I've landed on: &lt;strong&gt;a coding agent that runs unattended is an ops problem, not a coding problem.&lt;/strong&gt; Which is why the layers below matter more than which agent you pick.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gateway layer
&lt;/h2&gt;

&lt;p&gt;Routing through gateways instead of calling providers directly is the single biggest sanity improvement. I use three:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OpenRouter&lt;/strong&gt; — the default router for experimentation. The free tier covers low-stakes calls, and the model catalog means I can A/B test providers without touching config. &lt;a href="https://openrouter.ai/" rel="noopener noreferrer"&gt;OpenRouter&lt;/a&gt; - &lt;strong&gt;Merge&lt;/strong&gt; — my main paid gateway. One API for the models I use daily, with &lt;a href="https://agentjournal.dev/blog/merge-gateway-pro/" rel="noopener noreferrer"&gt;cost tracking&lt;/a&gt; across agents. At $10/mo it pays for itself in what it stops me from overspending on direct API calls. &lt;a href="https://merge.dev/" rel="noopener noreferrer"&gt;Merge&lt;/a&gt; - &lt;strong&gt;&lt;a href="https://agentjournal.dev/blog/vercel-ai-gateway-free/" rel="noopener noreferrer"&gt;Vercel AI Gateway&lt;/a&gt;&lt;/strong&gt; — for the projects already on Vercel's stack. Its free tier ($5 of credits per 30 days) handles the low-volume path fine; I only reach for it when the project is already there. &lt;a href="https://vercel.com/" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt; Why gateways and not direct API keys? Because &lt;strong&gt;every agent that holds a direct provider key is a credential you're one prompt injection away from leaking.&lt;/strong&gt; Gateways centralize the key, give you a kill switch, and make the bill auditable. This matters more than any model choice.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Credential handling (the part nobody writes about)
&lt;/h2&gt;

&lt;p&gt;This is where most agent setups are a disaster waiting to happen. My rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Secrets live in &lt;code&gt;pass&lt;/code&gt; / Bitwarden, never in agent context.&lt;/strong&gt; Agents get credentials via an injection layer at runtime, not by reading a &lt;code&gt;.env&lt;/code&gt; that sits in the repo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I maintain &lt;a href="https://github.com/ikkun1222/trustless" rel="noopener noreferrer"&gt;trustless&lt;/a&gt;&lt;/strong&gt; — an open-source credential broker that serves secrets to agents through a local proxy, with DLP scrubbing and per-call authorization. It exists because I watched agents echo API keys into logs, chat history, and debug output. Once per week was too often.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-lived keys are the enemy.&lt;/strong&gt; Refresh tokens that rotate, short-lived sessions, and per-service keys scoped to exactly one tool. When a key leaks (it will), the blast radius is one service, one function.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  MCP servers and tooling
&lt;/h2&gt;

&lt;p&gt;The MCP layer is where the real leverage is. Currently running:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Filesystem + git servers&lt;/strong&gt; — for agent access to repos without shelling out into the filesystem blindly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web search / fetch servers&lt;/strong&gt; — the ones that make agents useful for research without hallucinating URLs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Home-grown servers for my own infra&lt;/strong&gt; — including a server that wraps my credential broker so agents can request secrets through a structured interface instead of me pasting them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The lesson: &lt;strong&gt;a well-scoped MCP server beats a prompt telling the agent to "be careful" every time.&lt;/strong&gt; If the agent can't reach it, it can't break it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Networking and self-hosted pieces
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tailscale&lt;/strong&gt; — all agent access to my infra goes over the tailnet; SSH is not exposed publicly. Agents reach services by tailnet name, which also means the &lt;a href="https://agentjournal.dev/blog/tailnet-for-agents/" rel="noopener noreferrer"&gt;public attack surface&lt;/a&gt; is ~zero. &lt;a href="https://tailscale.com/" rel="noopener noreferrer"&gt;Tailscale&lt;/a&gt; - &lt;strong&gt;Cloudflare&lt;/strong&gt; — DNS, Pages (this site), Workers, and the edge layer. The free tier covers everything this site needs. &lt;a href="https://cloudflare.com/" rel="noopener noreferrer"&gt;Cloudflare&lt;/a&gt; - &lt;strong&gt;&lt;a href="https://agentjournal.dev/blog/qdrant-selfhosted/" rel="noopener noreferrer"&gt;Qdrant&lt;/a&gt; + Mem0&lt;/strong&gt; — self-hosted memory/vector layer for the automation agent. Running my own instance means the agent's memory never leaves my server. &lt;a href="https://qdrant.tech/" rel="noopener noreferrer"&gt;Qdrant&lt;/a&gt; - &lt;strong&gt;vLLM&lt;/strong&gt; — for the local models I run (a small Japanese-optimized model does embedding and light extraction work). Keeping boring inference local cuts gateway spend measurably.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The bill (real numbers)
&lt;/h2&gt;

&lt;p&gt;Monthly, as of this writing:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Gateways (Merge Pro + Vercel free tier + OpenRouter free)&lt;/td&gt;
&lt;td&gt;~$10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare (DNS + Pages + Workers)&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-hosted infra (cloud free tiers, one cheap VPS)&lt;/td&gt;
&lt;td&gt;~$5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$15/mo&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The trick is boring: &lt;strong&gt;route the expensive calls through the free tiers, self-host the repetitive work, and let the gateways absorb the variance.&lt;/strong&gt; I've seen the same setup run $200+/mo when the routing is lazy.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd change / what's broken
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context management is still the bottleneck.&lt;/strong&gt; Long-running agents degrade; I still restart sessions more than I'd like.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The agents occasionally fight each other&lt;/strong&gt; — two automation runs touching the same state. File locking and job ownership markers helped, but it's not solved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model quality is uneven across providers.&lt;/strong&gt; The gateway makes switching cheap, but "cheap to switch" means I spend more time evaluating than I'd like.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent you pick matters less than the plumbing around it: gateway routing, credential hygiene, scoped MCP access, and a tailnet. Get those right and you can swap models and agents freely — which is exactly what you'll want to do, because this space moves monthly.&lt;/p&gt;

&lt;p&gt;This is the first post in what will be a series on the details: MCP server design, gateway cost engineering, and credential handling for agents. If you're running agents in production too, &lt;a href="https://github.com/ikkun1222" rel="noopener noreferrer"&gt;I'd like to hear what your stack looks like&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: some links in this post are affiliate links — see &lt;a href="https://agentjournal.dev/disclosure/" rel="noopener noreferrer"&gt;disclosure&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://agentjournal.dev/blog/running-ai-coding-agents-in-production/" rel="noopener noreferrer"&gt;agentjournal.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>mcp</category>
      <category>security</category>
      <category>ai</category>
    </item>
    <item>
      <title>vLLM for one person: serving a small model</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Wed, 28 Oct 2026 00:15:37 +0000</pubDate>
      <link>https://dev.to/ikkun1222/vllm-for-one-person-serving-a-small-model-1bl9</link>
      <guid>https://dev.to/ikkun1222/vllm-for-one-person-serving-a-small-model-1bl9</guid>
      <description>&lt;p&gt;vLLM is the serving engine I use for the local models in my stack, and the honest summary is this: for one person running a small Japanese-optimized model for embeddings and light &lt;a href="https://agentjournal.dev/blog/gguf-quantization-intro/" rel="noopener noreferrer"&gt;inference&lt;/a&gt;, vLLM gives you high-throughput, continuously batched serving that you can point at and forget — once you treat the model as a service instead of a script. The hard part is not starting vLLM. It is remembering that a served model now needs monitoring, restarts, and version pinning, just like anything else you run.&lt;/p&gt;

&lt;p&gt;A lot of one-person setups never make that leap. They run a model inline in a Jupyter process, or start a server by hand when they need it. That works until it does not. Here is the workflow I landed on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why vLLM for a small model
&lt;/h2&gt;

&lt;p&gt;The pitch of vLLM is high-throughput inference with continuous batching. What that means in practice: rather than processing one request at a time or blocking while a batch fills, the engine keeps a queue of pending requests and packs new work into the gaps as each generated token completes. For my workload — repetitive inference, embeddings, and light extraction that runs constantly in the background — that throughput matters more than raw latency on any single request.&lt;/p&gt;

&lt;p&gt;It is suited to exactly the kind of jobs I run: many small requests that arrive steadily rather than one large burst. Continuous batching turns that stream of work into near-constant utilization of the GPU.&lt;/p&gt;

&lt;h2&gt;
  
  
  Picking the right model
&lt;/h2&gt;

&lt;p&gt;I serve a small Japanese-optimized model. The choice of Japanese optimization is not cosmetic: for &lt;a href="https://agentjournal.dev/blog/embedding-models-japanese/" rel="noopener noreferrer"&gt;Japanese text&lt;/a&gt;, generic English models degrade noticeably on embeddings and generation alike. Matching the model to the language of your documents is the difference between retrieval that works and retrieval that quietly loses precision.&lt;/p&gt;

&lt;p&gt;For a one-person setup the model should be small enough that you are never fighting your hardware. A small model on modest hardware beats a large model that you cannot keep running. The volume of embeddings in my memory stack is high, so throughput per token is the metric that matters, not benchmark scores.&lt;/p&gt;

&lt;h2&gt;
  
  
  Serving makes it a service
&lt;/h2&gt;

&lt;p&gt;The mental shift is the part nobody warns you about. Before serving, a model is a thing you invoke. After serving, it is a service with an address, and services fail in new ways. Three disciplines made the difference for me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring.&lt;/strong&gt; A silent serving process is a liability. Watch request latency, error rate, and queue depth, because those tell you when the model has degraded before a user notices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restarts.&lt;/strong&gt; The process will eventually die or hang. Plan for restarting it cleanly, the same way you would any other long-running service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version pinning.&lt;/strong&gt; Pin the exact model and engine versions you serve. A silent upgrade can change output behavior and corrupt embeddings without you realizing it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the same discipline I apply to the rest of the self-hosted stack — assume things break and make recovery boring. A served model is production infrastructure, so it gets treated like it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it fits in the stack
&lt;/h2&gt;

&lt;p&gt;The embeddings this model produces feed the memory layer, which is the highest-volume inference in my stack, so keeping it self-hosted and fast is a deliberate cost decision. Running it locally keeps that repetitive work off the paid path entirely.&lt;/p&gt;

&lt;p&gt;Serving a model properly is the difference between a working demo and infrastructure you can rely on. The engine is the easy part; the operational habits around it are what make it dependable.&lt;/p&gt;

&lt;p&gt;What does your smallest, most repetitive inference job look like — and is it still running as an unmanaged script?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://agentjournal.dev/blog/vllm-basics/" rel="noopener noreferrer"&gt;agentjournal.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>performance</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Japan public APIs for agents: e-Stat, EDINET</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Mon, 26 Oct 2026 00:15:40 +0000</pubDate>
      <link>https://dev.to/ikkun1222/japan-public-apis-for-agents-e-stat-edinet-4k2l</link>
      <guid>https://dev.to/ikkun1222/japan-public-apis-for-agents-e-stat-edinet-4k2l</guid>
      <description>&lt;h2&gt;
  
  
  The problem: Japanese data has no single doorway
&lt;/h2&gt;

&lt;p&gt;The first reflex for a Japanese company is to search the name and read whatever page comes back. That works for one company and collapses on the second: the pieces you need sit in four registers with four access rules, none of them a page you want to automate against. This is the map of which official source answers which question, and where each stops.&lt;/p&gt;

&lt;h2&gt;
  
  
  The identity layer: corporate numbers
&lt;/h2&gt;

&lt;p&gt;Company names are not unique and get transcribed inconsistently, so a name match is not an identity. The corporate number (法人番号) is the stable key: a 13-digit identifier from the National Tax Agency, reachable through the corporate number API with a free application ID, plus a CSV publication for bulk work. Use it as the join key for everything below. &lt;a href="https://agentjournal.dev/blog/houjin-bangou-api/" rel="noopener noreferrer"&gt;The corporate number API: looking up any Japanese company&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filings and financial statements: EDINET API v2
&lt;/h2&gt;

&lt;p&gt;For anything financial, the official route is EDINET, the FSA's disclosure system. Filings arrive as XBRL, and API v2 gives you a subscription key, date-range search and a flattened CSV form that most people prefer once they try parsing raw taxonomies. The sharp edges matter: the CSV is UTF-16, tab-delimited and quotes every field, so fix encoding and delimiter up front or the pipeline mangles numbers silently. &lt;a href="https://agentjournal.dev/blog/edinet-api-v2/" rel="noopener noreferrer"&gt;EDINET API v2: XBRL filings as CSV without the pain&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Statistics: e-Stat
&lt;/h2&gt;

&lt;p&gt;Population, GDP, CPI, employment: the official numbers come from the Statistics Bureau through the e-Stat API with an application ID. The part that trips people up is the two-step flow — ask the metadata endpoint what tables and categories exist, then request the data for the table you picked. Skip the first step and you are guessing at identifiers. &lt;a href="https://agentjournal.dev/blog/estat-api-practice/" rel="noopener noreferrer"&gt;Getting Japanese government statistics via the e-Stat API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Market data: an IRBANK-compatible endpoint
&lt;/h2&gt;

&lt;p&gt;Equities are where the global free feeds fail you: US and European tickers are covered, Japanese ones are missing, and the coverage wall appears only after you have built on them. The IRBANK-compatible endpoint fills that role — daily OHLCV, valuation metrics, dividends, margin balance and segment disclosure through one code per company, with no scraping of rendered pages. &lt;a href="https://agentjournal.dev/blog/irbank-api-jp-stocks/" rel="noopener noreferrer"&gt;IRBANK API: Japanese stock data without scraping&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The glue: where the application IDs live
&lt;/h2&gt;

&lt;p&gt;Application IDs and subscription keys are credentials, so they belong where the calling code cannot read them. A Google Apps Script web app holds a key server-side and exposes a thin endpoint at zero cost, with one trap: use a deployment ID, not the /dev URL, or it only works for you (&lt;a href="https://agentjournal.dev/blog/gas-web-app-proxy/" rel="noopener noreferrer"&gt;GAS web apps as API proxies&lt;/a&gt;). In my setup those IDs never sit in an agent's environment — they live in a credential store and arrive through a proxy, the pattern behind &lt;a href="https://github.com/ikkun1222/trustless" rel="noopener noreferrer"&gt;trustless&lt;/a&gt;. For lightweight record stores rather than acquisition, the same CLI access pays off (&lt;a href="https://agentjournal.dev/blog/kintone-cli/" rel="noopener noreferrer"&gt;kintone from the CLI&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  The shape they share
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Official first. An official feed has a stated contract and a maintainer paid to publish the numbers; scraping is the fallback.&lt;/li&gt;
&lt;li&gt;Identity before metrics. Resolve the entity key, then attach filings, statistics and prices. Merging without a stable key is how double counting starts.&lt;/li&gt;
&lt;li&gt;Read the format spec, not the sample. UTF-16 and tab delimiters look fine in a preview and break a loader.&lt;/li&gt;
&lt;li&gt;Treat rate limits as a schedule — they tell you the cadence the pipeline should run at.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these is turnkey, and each is documented better in Japanese than in English — a real cost when you work in English. The alternative is scrapers against four rendered sites, and that bill arrives every time a layout changes.&lt;/p&gt;

&lt;p&gt;Which country's official data have you had to wire in for the same reason — and did an official API exist, or did you end up maintaining scrapers?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://agentjournal.dev/blog/japan-public-apis-for-agents/" rel="noopener noreferrer"&gt;agentjournal.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>agents</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>OpenCode's leak was Console, not your CLI</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Sat, 24 Oct 2026 02:00:50 +0000</pubDate>
      <link>https://dev.to/ikkun1222/opencodes-leak-was-console-not-your-cli-4j3n</link>
      <guid>https://dev.to/ikkun1222/opencodes-leak-was-console-not-your-cli-4j3n</guid>
      <description>&lt;p&gt;The OpenCode story that went around on 2026-10-17 was reported as "OpenCode hacked, user credentials leaked", and the first thing most people did was look at the CLI they run every day. That is not where the leak was. The exposed system was &lt;strong&gt;OpenCode Console&lt;/strong&gt;, an optional hosted service where you connect provider accounts so a team can share them. Console stores those provider credentials on its side. The local coding agent stores your keys on your disk and talks to whichever endpoint your config names.&lt;/p&gt;

&lt;p&gt;I use the CLI daily and I have never connected a provider to Console. I still spent twenty minutes proving that to myself, because the two products share a name and nobody's first reaction to a breach notice is "let me map the architecture". This is the check I ran, with the numbers, because "I think I'm fine" is not an answer you can act on.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the vendor actually said
&lt;/h2&gt;

&lt;p&gt;The team's statement, posted in the discussion thread, says unauthorized access to "a legacy system that we were in the process of decommissioning", and that "third party provider keys (eg OpenAI, Anthropic) saved in your workspace may have been exposed". The instruction was to revoke and replace those keys at the provider, update anything using them, and review recent usage.&lt;/p&gt;

&lt;p&gt;An OpenAI customer posted the email they received, which describes the same event from the other side: unauthorized access to a system that stored customer API keys, an affected key belonging to their organization, and confirmation that the key had already been disabled. That is the shape of a real incident: not a model doing something strange, but a credential store losing custody of credentials.&lt;/p&gt;

&lt;p&gt;The thread hit 975 points with 37 top-level comments, and most of the argument was not about the breach. It was about which product had been breached and why the announcement did not say so in the first line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the name cost everyone a day
&lt;/h2&gt;

&lt;p&gt;The documentation for Console is explicit about the design: connect provider accounts you already pay for, "Console stores the credential and serves each provider through" the gateway, and the models are shared across a workspace. Storing the credential is the feature. Sharing it is the point.&lt;/p&gt;

&lt;p&gt;The local agent does something different. It keeps provider credentials in a file on your machine and sends requests to the base URL configured for that provider. Whether those requests reach the vendor's hosted gateway, a local proxy, or a third-party API depends entirely on your config. That single sentence resolves most of the confusion, and it is missing from the announcement.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four checks I ran
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Command shape&lt;/th&gt;
&lt;th&gt;Result on my machine&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Which endpoint do the stored keys authenticate to&lt;/td&gt;
&lt;td&gt;read &lt;code&gt;baseURL&lt;/code&gt; per provider in the agent config&lt;/td&gt;
&lt;td&gt;all three point at &lt;code&gt;127.0.0.1:8787&lt;/code&gt;, a local gateway, not the hosted service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How many credentials are on disk&lt;/td&gt;
&lt;td&gt;list the auth file entries&lt;/td&gt;
&lt;td&gt;3 provider entries, file mode 600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra copies of the same keys&lt;/td&gt;
&lt;td&gt;scan projects, skills, dotfiles for the exact key strings&lt;/td&gt;
&lt;td&gt;0 hits across 38,734 files, 0 in shell history&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keys committed to the config repository&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;git ls-files&lt;/code&gt; in the config directory&lt;/td&gt;
&lt;td&gt;49 tracked files, auth file not among them&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The endpoint check is the one that settles it. My three entries are for a gateway, a model lab, and a hosting provider, and every one of them is routed through a process on loopback, which means the key is presented to code I run, not to a hosted workspace that stores it for me. If your config points at the vendor's gateway URL and you went through their connect flow, you are in the other category and you should be revoking keys today.&lt;/p&gt;

&lt;p&gt;The copy scan is the check most people skip. A key that leaked into a repository, a &lt;code&gt;.env&lt;/code&gt; committed once and deleted later, or a shell history file is a credential with a second life. Scanning 38,734 files takes about a minute and returns a number you can compare later.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the checks do not prove
&lt;/h2&gt;

&lt;p&gt;Local mode is not "safe". Every one of those three keys is stored as plaintext JSON, readable by anything running as my user. A process I install tomorrow can read them without asking. The 600 file mode stops other users on the machine, not me, and not a compromised package running under my uid.&lt;/p&gt;

&lt;p&gt;One detail from the same scan makes that concrete: there are two auth files, and the backup copy holds a &lt;strong&gt;different&lt;/strong&gt; value for one of the three providers. A rotation happened on 2026-10-20, and the retired value is still sitting on disk in a second file. I have not deleted it yet, because I want to confirm with the gateway that the old value is actually dead, and "probably dead" is how these things turn into incidents later.&lt;/p&gt;

&lt;p&gt;The config directory is also a public repository. The auth file is not tracked, which is correct, but a public repo with 49 tracked files is one careless &lt;code&gt;git add -A&lt;/code&gt; away from publishing something that should not be published. That is a control I should tighten, not a fact I should feel good about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five steps if you use any hosted key service
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Decide which product you actually used. A CLI is not a hosted workspace, and the difference is in your config, not in your memory.&lt;/li&gt;
&lt;li&gt;If you connected a provider to a hosted service, revoke the key at the provider and issue a new one. Disabling it in the service is not enough; the provider's dashboard is the authority.&lt;/li&gt;
&lt;li&gt;Read the usage graphs at each provider for the last two weeks. A leaked key usually shows up as a spike before it shows up as an email.&lt;/li&gt;
&lt;li&gt;Look for second copies: repositories, backups, shell history, CI secrets, that one &lt;code&gt;.env&lt;/code&gt; on the old branch.&lt;/li&gt;
&lt;li&gt;For anything an agent uses, prefer a path where the agent never holds the key. A local proxy that injects the credential at the network layer means the blast radius of a compromised agent is requests, not the credential itself.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last one is the lesson I keep relearning. Sandboxing the agent does not help when the key lives in a service the agent can reach. Removing the key from the agent's reach does.&lt;/p&gt;

&lt;p&gt;If you went through this on the 17th: did you find out from the vendor, from your provider, or from the bill?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://agentjournal.dev/blog/opencode-console-leak-scope/" rel="noopener noreferrer"&gt;agentjournal.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>agents</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Cloudflare's audit skill needs a different harness</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Fri, 23 Oct 2026 18:26:28 +0000</pubDate>
      <link>https://dev.to/ikkun1222/cloudflares-audit-skill-needs-a-different-harness-3hb8</link>
      <guid>https://dev.to/ikkun1222/cloudflares-audit-skill-needs-a-different-harness-3hb8</guid>
      <description>&lt;p&gt;Cloudflare published &lt;a href="https://github.com/cloudflare/security-audit-skill" rel="noopener noreferrer"&gt;the skill&lt;/a&gt; that seeded their vulnerability discovery harness, and it is a good skill: six phases, adversarial validation, structured verdicts, and an explicit instruction to report what you could not verify instead of guessing. I ran it on my own repository and it found real bugs.&lt;/p&gt;

&lt;p&gt;It did not run out of the box. Not because the skill is wrong, but because it assumes three things about the environment that a normal developer setup does not have: an agent that can spawn isolated parallel subagents, an OS-enforced sandbox for executing target code, and Node available on the parent side to validate the artifacts. I had a laptop-grade agent CLI and no sandbox. Most of the work was building those three things, and that work is reusable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the skill actually requires
&lt;/h2&gt;

&lt;p&gt;Straight from the repository, the requirements are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a coding agent whose model supports tool use and parallel sub-agents&lt;/li&gt;
&lt;li&gt;Node.js, for the two zero-dependency validators that check the coverage ledger and the findings file against their schemas&lt;/li&gt;
&lt;li&gt;an OS-enforced sandbox that disables external networking, uses a sanitized allowlisted environment, enforces resource limits, and permits writes only to assigned scratch paths&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last one is not optional advice. The skill says that without all of those controls it will not execute target code, and the affected findings stay at &lt;code&gt;needs_validation&lt;/code&gt;. That is the correct behaviour: an audit that runs a target's test suite unsandboxed is a supply chain incident with extra steps.&lt;/p&gt;

&lt;p&gt;The pipeline itself is six phases: reconnaissance into an architecture map and a coverage ledger, coverage-led hunting with isolated hunters, candidate validation by a fresh verifier, structured output to &lt;code&gt;findings.json&lt;/code&gt;, independent verification of the final source claims, and reporting derived from the verified records. Verdicts are &lt;code&gt;confirmed&lt;/code&gt;, &lt;code&gt;needs_validation&lt;/code&gt;, and &lt;code&gt;rejected&lt;/code&gt;, and the skill is explicit that a rejected candidate is a disproved candidate, not a harmless one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gap 1: the agent runtime
&lt;/h2&gt;

&lt;p&gt;The skill delegates. A single agent doing all the hunting sequentially would take days, and worse, it would review its own work. So the first question was which CLI could actually spawn isolated subagents with a skill attached and permission restrictions.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;pi&lt;/td&gt;
&lt;td&gt;no task tool, so no subagents. Cannot run this skill&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Command Code&lt;/td&gt;
&lt;td&gt;has a task tool, but the Go quota throttled runs into stopping partway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;opencode (V1)&lt;/td&gt;
&lt;td&gt;skill tool, task tool and permission maps all present. Completed all three runs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The detail that made opencode work is the role split. I defined two subagent roles as files: &lt;code&gt;security-hunter&lt;/code&gt; and &lt;code&gt;security-verifier&lt;/code&gt;. Both are read-only on the code, and the verifier is configured with &lt;code&gt;task: false&lt;/code&gt; so it cannot spawn children of its own. That is what makes verification independent: the agent that checks a finding has no way to delegate the checking back to the agent that produced it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gap 2: the sandbox
&lt;/h2&gt;

&lt;p&gt;I tried bubblewrap first, because it is the lightest option and it needs no daemon. It failed immediately on Ubuntu with &lt;code&gt;loopback: Failed RTM_NEWADDR&lt;/code&gt;. The cause is a kernel setting that restricts unprivileged user namespaces (&lt;code&gt;kernel.apparmor_restrict_unprivileged_userns=1&lt;/code&gt;), which turns bwrap into a tool that cannot set up its own network namespace.&lt;/p&gt;

&lt;p&gt;Docker worked, with three corrections that cost me a round each:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;--tmpfs&lt;/code&gt; mounts are &lt;code&gt;noexec&lt;/code&gt; by default. The build tools inside the scratch directory then fail with permission errors that look like compilation problems. It needs &lt;code&gt;exec&lt;/code&gt; in the mount options.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;debian:bookworm-slim&lt;/code&gt; does not ship &lt;code&gt;sqlite3&lt;/code&gt;. The target's test suite had database tests, and about a third of them failed for a reason unrelated to security. Adding the package fixed the noise.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GOTOOLCHAIN=auto&lt;/code&gt; tries to download a toolchain, which cannot work when the container has no network. Pinning &lt;code&gt;GOTOOLCHAIN=local&lt;/code&gt; and bind-mounting a &lt;code&gt;GOROOT&lt;/code&gt; that matches the module's Go directive was the fix. Dependencies were vendored on the host before the snapshot, so the container never needs to fetch.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The resulting invocation, trimmed to the parts that matter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--network&lt;/span&gt; none &lt;span class="nt"&gt;--read-only&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tmpfs&lt;/span&gt; /scratch:rw,exec,size&lt;span class="o"&gt;=&lt;/span&gt;2g,mode&lt;span class="o"&gt;=&lt;/span&gt;1777 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,src&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$SNAPSHOT&lt;/span&gt;,dst&lt;span class="o"&gt;=&lt;/span&gt;/target,readonly &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;,src&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$TOOLCHAIN&lt;/span&gt;,dst&lt;span class="o"&gt;=&lt;/span&gt;/toolchain,readonly &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;GOROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/toolchain &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/toolchain/bin:/usr/local/bin:/usr/bin:/bin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/scratch &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;GOTOOLCHAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;GOFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-mod&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vendor &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--user&lt;/span&gt; 1000:1000 &lt;span class="nt"&gt;--cap-drop&lt;/span&gt; ALL &lt;span class="nt"&gt;--security-opt&lt;/span&gt; no-new-privileges &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--memory&lt;/span&gt; 2g &lt;span class="nt"&gt;--cpus&lt;/span&gt; 2 &lt;span class="nt"&gt;--pids-limit&lt;/span&gt; 256 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;local&lt;/span&gt;/audit-runner:1 /bin/sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'cp -a /target/. /scratch/t/ &amp;amp;&amp;amp; cd /scratch/t &amp;amp;&amp;amp; &amp;lt;command&amp;gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The target is mounted read-only and copied into a writable scratch directory per run. The snapshot comes from &lt;code&gt;git archive&lt;/code&gt;, so the audit sees one commit and not my working tree. Because &lt;code&gt;.git&lt;/code&gt; is absent from the snapshot, the prompt has to state the source ref explicitly, otherwise the agent cannot say what it audited.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gap 3: who validates the validators
&lt;/h2&gt;

&lt;p&gt;The two &lt;code&gt;.cjs&lt;/code&gt; validators are the only objective gate in the pipeline. If they run, a malformed &lt;code&gt;findings.json&lt;/code&gt; cannot be reported as a result. If they do not, you get a report that looks structured and means nothing. So the parent runs them, not the agent, and both must exit zero before a run is done.&lt;/p&gt;

&lt;p&gt;The same logic applies to the findings themselves. A &lt;code&gt;confirmed&lt;/code&gt; verdict from a delegated agent is a claim. For each confirmed item I wrote a small reproduction test as a separate file against the original commit, then ran the same test against the fixed commit. Red before, green after is the only evidence I accept for a security fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it costs in time
&lt;/h2&gt;

&lt;p&gt;Measured on a Go codebase of moderate size:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Profile&lt;/th&gt;
&lt;th&gt;Wall clock&lt;/th&gt;
&lt;th&gt;Agents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;quick, scoped&lt;/td&gt;
&lt;td&gt;about 40 minutes&lt;/td&gt;
&lt;td&gt;fewer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;quick, full&lt;/td&gt;
&lt;td&gt;about 52 minutes&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;standard, full&lt;/td&gt;
&lt;td&gt;about 2.9 hours&lt;/td&gt;
&lt;td&gt;up to 60&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The repository notes that a single run finds roughly half of what repeated runs find. My own numbers agree: the second and third runs produced findings the first did not, and one of them reversed a rejection from the first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things I would do differently
&lt;/h2&gt;

&lt;p&gt;Do not touch git while a delegated agent is working. I ran a checkout during a run and the agent kept reading a stale HEAD, which produced findings against code that no longer existed. Fetch and reset before or after a run, never during.&lt;/p&gt;

&lt;p&gt;Keep the agent's output directory outside the target repository. The skill defaults to a path under your home directory for a reason.&lt;/p&gt;

&lt;p&gt;Treat the profile as a coverage decision, not a speed setting. A quick run that returns few findings may have simply not looked.&lt;/p&gt;

&lt;p&gt;The findings from the three runs, including the one verdict that reversed between runs, are in &lt;a href="https://agentjournal.dev/blog/three-audit-runs-rejected-then-high/" rel="noopener noreferrer"&gt;a separate post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have tried to run an agent-oriented security skill on your own projects: which requirement stopped you first, the subagents or the sandbox?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://agentjournal.dev/blog/cloudflare-audit-skill-harness/" rel="noopener noreferrer"&gt;agentjournal.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>agents</category>
      <category>devops</category>
      <category>ai</category>
    </item>
    <item>
      <title>EDINET API v2: XBRL filings as CSV without the pain</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Fri, 23 Oct 2026 17:27:43 +0000</pubDate>
      <link>https://dev.to/ikkun1222/edinet-api-v2-xbrl-filings-as-csv-without-the-pain-1pkk</link>
      <guid>https://dev.to/ikkun1222/edinet-api-v2-xbrl-filings-as-csv-without-the-pain-1pkk</guid>
      <description>&lt;h2&gt;
  
  
  The problem: Japanese financial filings in a usable shape
&lt;/h2&gt;

&lt;p&gt;If you want annual or quarterly financials for Japanese companies, the filings are real and public, but they sit in XBRL. XBRL is great for regulators and lousy for a quick pipeline: it is verbose, namespaced, and tied to taxonomy dictionaries. My first attempts to pull company financials meant wrestling XBRL documents and their taxonomies into something I could actually compute on. It was painful enough that I kept looking for a shortcut, and the right shortcut is the official one.&lt;/p&gt;

&lt;p&gt;The direct answer: EDINET API v2 is Japan's official financial filings API, and it exposes XBRL metadata and documents for essentially all filers going back about ten years, free. For bulk work, the type=5 form delivers the data as CSV so I can skip the full XBRL parse when all I need is the numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the CSV simplicity hides
&lt;/h2&gt;

&lt;p&gt;The type=5 CSV form is what makes EDINET usable as a data source rather than a document dump. It flattens financial data into a CSV-like payload that I can load into analysis directly. But "CSV-like" deserves the qualifier, because the format has quirks that will break a naive reader.&lt;/p&gt;

&lt;p&gt;Three things bit me. The payload is UTF-16 encoded, not UTF-8. The delimiter is a tab, not a comma. And every field is quoted, like a fully escaped TSV. My normal CSV parser choked on the encoding on the first try, and even after fixing that, the all-fields-quoted layout needs the right handling to split lines cleanly. Once I put the splitlines handling in place, the data comes through reliably, but it is precisely the kind of edge case that makes a canned "just use csv.reader" approach fail silently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auth lives in a header, not a query param
&lt;/h2&gt;

&lt;p&gt;A detail that cost me a round-trip: the API authenticates with an Ocp-Apim-Subscription-Key header, not a query parameter. If you are used to appending a key to the URL, you will get a rejection before you ever reach the data. It is a small thing, but it is the kind of contract detail that determines whether an integration is smooth or a morning of debugging. Because the credential goes in a header, it stays out of the URL, which also keeps it out of the obvious places where query strings end up logged. I still keep the key behind my normal key-handling practices rather than embedding it in anything that ships. I describe it as the subscription key; the actual value never leaves my controlled environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bulk ingestion and date-range search
&lt;/h2&gt;

&lt;p&gt;The API supports searching by date range, and that is the lever for doing this at scale. I ingest financial data for hundreds of companies programmatically, which would be miserable one filing at a time. Searching by range and pulling the type=5 CSV form means I can batch the work instead of scripting per-company lookups. Searching by date also maps cleanly onto a recurring job: I know what moved since the last run, and I fetch only the new material.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why official EDINET wins
&lt;/h2&gt;

&lt;p&gt;The case for EDINET over an unofficial feed is the same one I apply across data sources: when an official API exists with a stated contract and a subscription-key auth model, it is more reliable and it avoids the upkeep of chasing a feed that might change without notice. An unofficial EDINET mirror can work, but it is a source of truth I do not control. My preference is the official API first, unofficial only when there is no other choice.&lt;/p&gt;

&lt;p&gt;None of this is to say the official API is turnkey — the UTF-16, tab-delimited, every-field-quoted CSV is a reminder that "official" and "ergonomic" are different things. But it is official, it is free, and it gives me a ten-year window of filings. That combination is hard to beat.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rest of the Japanese-API cluster
&lt;/h2&gt;

&lt;p&gt;The other pieces I use with this one: &lt;a href="https://agentjournal.dev/blog/estat-api-practice/" rel="noopener noreferrer"&gt;Getting Japanese government statistics via the e-Stat API&lt;/a&gt;, &lt;a href="https://agentjournal.dev/blog/houjin-bangou-api/" rel="noopener noreferrer"&gt;the corporate number API&lt;/a&gt;, &lt;a href="https://agentjournal.dev/blog/irbank-api-jp-stocks/" rel="noopener noreferrer"&gt;IRBANK API&lt;/a&gt;, &lt;a href="https://agentjournal.dev/blog/gas-web-app-proxy/" rel="noopener noreferrer"&gt;GAS web app proxies&lt;/a&gt; and &lt;a href="https://agentjournal.dev/blog/kintone-cli/" rel="noopener noreferrer"&gt;kintone from the CLI&lt;/a&gt;. Full map: &lt;a href="https://agentjournal.dev/blog/japan-public-apis-for-agents/" rel="noopener noreferrer"&gt;Japan's public APIs for agents&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For anyone pulling Japanese financials, my advice is: start from EDINET API v2, use the type=5 CSV form, and fix the encoding and delimiter up front. What has your experience been with XBRL taxonomies versus the flattened CSV — is the raw XBRL worth the extra parsing for you?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://agentjournal.dev/blog/edinet-api-v2/" rel="noopener noreferrer"&gt;agentjournal.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>agents</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Audit Gap Decides the Platform</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Fri, 23 Oct 2026 17:13:16 +0000</pubDate>
      <link>https://dev.to/ikkun1222/the-audit-gap-decides-the-platform-5hl0</link>
      <guid>https://dev.to/ikkun1222/the-audit-gap-decides-the-platform-5hl0</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: In August 2026, Claude Cowork's activity is excluded from Compliance API and audit logs even on Anthropic Enterprise. ChatGPT Work — the equivalent non-engineer desktop surface — is covered. If your organization handles information that must not leak, this single fact outweighs every model-quality argument.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One-line summary&lt;/strong&gt;: A surface you cannot audit cannot be your primary deployment — regardless of which vendor makes it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is Part 1 of a series on selecting an agent platform for an organization with no dedicated engineering staff (see the &lt;a href="https://agentjournal.dev/blog/enterprise-agent-selection/" rel="noopener noreferrer"&gt;series overview&lt;/a&gt; for context). The client details are anonymized; the vendor facts are not, and each one below is dated and checkable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "audit coverage" means here
&lt;/h2&gt;

&lt;p&gt;Three distinct things get called "audit," and confusing them causes bad selections:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Audit logs / Compliance API&lt;/strong&gt; — organization-level records of user activity, pullable into your SIEM via API. This is what a regulator or your own incident review needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenTelemetry events&lt;/strong&gt; — developer-oriented telemetry. Useful, but not a substitute: coverage differs, retention is yours to build, and it is not designed as an evidence trail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local history&lt;/strong&gt; — conversations stored on the user's machine. Admins can't query it centrally at all.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When I say "auditable" in this series, I mean #1. The other two have real value — OTel feeds dashboards and alerts, local history serves the individual user — but neither answers the question "show me everything that happened in this workspace last month."&lt;/p&gt;

&lt;h2&gt;
  
  
  Why staff-less organizations feel this hardest
&lt;/h2&gt;

&lt;p&gt;In an engineering org, audit gaps can be compensated: someone wires up OTel pipelines, builds retention, writes detection rules. It's work, but it's work their people can do.&lt;/p&gt;

&lt;p&gt;In an organization without dedicated engineering staff, there is no compensation path. The vendor's audit coverage &lt;em&gt;is&lt;/em&gt; your audit coverage. Whatever the admin console shows you is the whole picture, forever. That asymmetry is why audit coverage sits above model quality in this series' evaluation order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model benchmarks change monthly; audit scope is structural and survives contract renewals.&lt;/li&gt;
&lt;li&gt;You can switch models inside a platform; you cannot self-build compliance coverage for a surface where the vendor records nothing.&lt;/li&gt;
&lt;li&gt;When something goes wrong — and at some point something goes wrong everywhere — the question "what exactly happened?" needs an answer from records, not from users' memories.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The facts on the table (as of August 2026)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Surface&lt;/th&gt;
&lt;th&gt;Anthropic&lt;/th&gt;
&lt;th&gt;OpenAI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Chat (web)&lt;/td&gt;
&lt;td&gt;Compliance API covered&lt;/td&gt;
&lt;td&gt;Compliance API covered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Non-engineer desktop app&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Claude Cowork: NOT covered&lt;/strong&gt; (OTel only; local history is device-local)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;ChatGPT Work desktop: covered&lt;/strong&gt; (workspace policies apply)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coding CLI&lt;/td&gt;
&lt;td&gt;Claude Code: managed settings + OTel&lt;/td&gt;
&lt;td&gt;Codex: requirements.toml + Compliance API (&lt;code&gt;CODEX_LOG&lt;/code&gt;, &lt;code&gt;CODEX_SECURITY_LOG&lt;/code&gt;, ~30-day retention)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud agents&lt;/td&gt;
&lt;td&gt;Cowork cloud (beta): not covered&lt;/td&gt;
&lt;td&gt;Codex cloud: covered&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three observations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cowork's exclusion survives the top plan tier.&lt;/strong&gt; Anthropic Enterprise adds SCIM, custom roles, and Analytics API — and still documents Cowork activity as out of scope for Compliance API. This is a structural gap, not a pricing gap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI keeps both audiences inside one auditable container.&lt;/strong&gt; Non-engineers (Work) and developers (Codex) both land in Compliance API, with Codex events arriving as typed event streams (&lt;code&gt;CODEX_LOG&lt;/code&gt;, security events separately).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cowork does have OpenTelemetry&lt;/strong&gt;, and it's the only telemetry route even on cheaper plans. It's real, and worth wiring into a collector if you allow Cowork at all — but treat it as monitoring, not evidence.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Retention changes the math
&lt;/h3&gt;

&lt;p&gt;Coverage is only half of it; retention is the other half. An audit log that expires in 30 days supports incident triage but not annual review or a formal inquiry. When comparing platforms, ask three questions as one bundle: What is recorded? Per surface? For how long, and in what export format? A platform that answers all three consistently is worth more than one with deeper features and vaguer logging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify it yourself before signing
&lt;/h2&gt;

&lt;p&gt;None of the facts above require trusting this article. The verification takes under an hour with trial or demo tenants:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Open the admin console&lt;/strong&gt; for each plan tier you're considering. List every surface (web chat, desktop app, CLI, cloud agents) visible to a regular user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For each surface, generate activity&lt;/strong&gt; — one conversation, one agent task — then search for it in the audit log / Compliance API export.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Note what's missing.&lt;/strong&gt; Anything absent from step 2's results is a surface you cannot audit. Write the list down; this becomes a policy input, not a footnote.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pull one real export&lt;/strong&gt; via the API and check field completeness (actor, timestamp, action, object). Screenshots in a console UI don't count until the export works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check retention terms&lt;/strong&gt; in writing, in the contract paperwork — not just marketing pages.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Step 3's list is the deliverable that matters. In the selection process this series documents, that list contained Cowork — and that single line item reordered the whole evaluation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do with excluded surfaces
&lt;/h2&gt;

&lt;p&gt;Excluding a surface entirely isn't always possible; users arrive with preferences. The mitigation ladder, weakest to strongest:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Exclude from regulated work by policy&lt;/strong&gt; — written rule, enforced by review, technically unenforced. Fragile, but honest about its weakness.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read-only limitation&lt;/strong&gt; — allowed for summarizing public material, banned for anything sensitive. Requires information-classification discipline (see Part 6 for why that's hard without staff).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Telemetry-first conditional allowance&lt;/strong&gt; — OTel wired into your collector before first use, alerting on volume anomalies, plus periodic manual review. This is the strongest available posture for Cowork today, and it still isn't evidence-grade.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Whatever rung you choose, the decision should be recorded with a date and a re-check trigger. Vendor terms move; a policy frozen in August 2026 shouldn't silently govern August 2027.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Vendor terms change without notice; re-check the admin docs before signing anything.&lt;/li&gt;
&lt;li&gt;This article reflects documentation review and sandbox-tier verification, not a full Enterprise deployment. Pilot results follow in later parts.&lt;/li&gt;
&lt;li&gt;Nothing here claims Claude models are weaker, nor that Cowork is unsafe. It is a record that its audit story did not match this particular constraint set. If Anthropic brings Cowork into Compliance API coverage, Part 6 lists exactly how the conclusion gets re-evaluated.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Coming August 27: Part 2 — One Admin Console Beats Three&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Takeaway&lt;/strong&gt;: Check Compliance API coverage per surface, per plan tier, before comparing anything else. One excluded surface disqualifies it from hosting regulated work — and exclusions don't always disappear at higher tiers.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://agentjournal.dev/blog/audit-gap-decides-platform/" rel="noopener noreferrer"&gt;agentjournal.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>api</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Vercel AI Gateway free tier: $5 of credits, real limits</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Fri, 23 Oct 2026 17:05:12 +0000</pubDate>
      <link>https://dev.to/ikkun1222/vercel-ai-gateway-free-tier-5-of-credits-real-limits-56bf</link>
      <guid>https://dev.to/ikkun1222/vercel-ai-gateway-free-tier-5-of-credits-real-limits-56bf</guid>
      <description>&lt;p&gt;"Free tier" on a gateway usually means one of two things: a meter you discover after the bill, or a teaser that evaporates the moment you enter a card. The &lt;a href="https://vercel.com/" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt; AI Gateway free tier is neither — it is $5 of credits per 30 days, real and observed, with no card required. The catch is that the free credits stop once a card is registered. I use it for the projects already on Vercel's stack, and it works fine as long as I respect that one rule and keep the gateway off the default path.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the $5 credit tier actually is
&lt;/h2&gt;

&lt;p&gt;Every 30 days, the account gets $5 in gateway credits. For a low-volume path — a dashboard endpoint, a small agent script attached to a Vercel deployment — that covers the month without drama. The key word is low-volume. I treat the credit balance as the spec: if a project's gateway traffic fits inside $5 of credits a month, it can live on the free tier. If it cannot, it belongs on the paid gateway, not here. There is no billing surprise, because there is no card attached.&lt;/p&gt;

&lt;h2&gt;
  
  
  The card-on-file trap
&lt;/h2&gt;

&lt;p&gt;The behavior I observed: the free tier ends once a card is registered. Not "the credits keep coming and the card catches the overflow" — the free credits simply stop. That is the single most important detail, because every signup flow nudges you toward adding a card for overage protection. On this gateway, that safety net cancels the free tier. I keep the free-tier projects card-free, and the moment a project needs overage protection, it graduates to the paid gateway instead of adding the card.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the key lives
&lt;/h2&gt;

&lt;p&gt;The gateway key goes straight into my password manager — never into the repository, never into agent context. Agents that need it fetch it from the credential broker at runtime, so a checked-in file or a pasted env var is not how the key travels. That is the same discipline I described in my post on &lt;a href="https://agentjournal.dev/blog/credential-management-for-ai-agents/" rel="noopener noreferrer"&gt;credential management for AI agents&lt;/a&gt;, and it matters more with gateways than with direct providers, because one gateway key opens every model behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens on a 429
&lt;/h2&gt;

&lt;p&gt;Rate limits are the routine failure mode, and the handling is configured, not improvised. When the gateway returns HTTP 429, the fallback chain routes the call to a different model provider and the work continues. The gateway is not my default gateway; it is used only for projects already on the Vercel stack, and only for the low-volume path within those projects. A 429 on that path is an inconvenience handled in the chain, not a stop-the-world event. That is why I keep the free tier on the chain instead of in it as the only option.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it is not my default gateway
&lt;/h2&gt;

&lt;p&gt;Workflow preference: the projects on Vercel's stack get the gateway because it sits next to the deployment, not because it is the best router I have. Everything else routes through my main gateway, with its own cost tracking and fallback chain. Keeping the Vercel gateway scoped to its own projects means the $5 credit tier never has to carry more than it is sized for, and the card-on-file rule never gets tested by accident.&lt;/p&gt;

&lt;p&gt;The $5 credit tier is honest about what it is — a small monthly allowance with a sharp edge. Respect the edge, and it quietly covers a whole class of low-volume work. Have you checked whether your gateway's "free tier" actually survives adding a card?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: some links in this post are affiliate links — see &lt;a href="https://agentjournal.dev/disclosure/" rel="noopener noreferrer"&gt;disclosure&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://agentjournal.dev/blog/vercel-ai-gateway-free/" rel="noopener noreferrer"&gt;agentjournal.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>vercel</category>
      <category>cloud</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Jev vs a 310M encoder I trained myself: 750 rows, three tasks, two different winners</title>
      <dc:creator>Ikkun</dc:creator>
      <pubDate>Mon, 21 Sep 2026 02:20:55 +0000</pubDate>
      <link>https://dev.to/ikkun1222/jev-vs-a-310m-encoder-i-trained-myself-750-rows-three-tasks-two-different-winners-242e</link>
      <guid>https://dev.to/ikkun1222/jev-vs-a-310m-encoder-i-trained-myself-750-rows-three-tasks-two-different-winners-242e</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: I ran three Japanese classification tasks (250 rows each, same gold labels) through six systems. A 310M encoder I fine-tuned on 250 labels beat TypeSafe's Jev on topic classification by &lt;strong&gt;+12.0 points&lt;/strong&gt; (McNemar p=0.00007) and was &lt;strong&gt;4–20× faster&lt;/strong&gt; — but on the two polarity tasks it only tied Jev. The zero-shot open-source models everyone is writing about lost everywhere: a GLiNER-family classifier scored 62.8 / 87.2 / 44.8 against Jev's 76.8 / 94.4 / 74.0. The same architecture family, trained on my labels, scored 88.8 / 92.8 / 75.2.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One-line summary&lt;/strong&gt;: A decision API wins when you have no labels; a small trained encoder wins when you have a few hundred — and which one wins on a given task is decided by the shape of the task, not by which model is trending.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The question behind the hype
&lt;/h2&gt;

&lt;p&gt;Jev (TypeSafe AI's System One) is genuinely good, and it is cheap: $0.042 per 1M input tokens with output free, roughly 32k of context, a confidence number in the response, and no training data required. If you read the last two weeks of posts about it, the conclusion is "use Jev."&lt;/p&gt;

&lt;p&gt;I use Jev. But I had a pile of results from my own tasks that did not fit that conclusion, and one of them was embarrassing enough that I re-ran it twice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;char n-gram + logistic regression baseline&lt;/strong&gt; beat Jev on long-document topic classification by 11.6 points (0.884 vs 0.768).&lt;/li&gt;
&lt;li&gt;The same baseline lost to Jev by 20.8 points on short-text polarity (0.736 vs 0.944).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A 5-minute linear model beating a decision API on one task and losing badly on the next is not a story about either model. It is a story about &lt;strong&gt;task shape&lt;/strong&gt;. So I designed the experiment I should have run first: hold the answers fixed, hold the rows fixed, and separate the two axes that actually differ — &lt;em&gt;labels or no labels&lt;/em&gt;, and &lt;em&gt;topic or meaning&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I measured, and how
&lt;/h2&gt;

&lt;p&gt;Three Japanese datasets, 250 rows each, frozen:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Shape&lt;/th&gt;
&lt;th&gt;Classes&lt;/th&gt;
&lt;th&gt;Chance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;livedoor news corpus&lt;/td&gt;
&lt;td&gt;long-document topic (mean 1,174 chars)&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;11.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rakuten reviews&lt;/td&gt;
&lt;td&gt;short-text polarity (mean 138 chars)&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;50.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;chabsa&lt;/td&gt;
&lt;td&gt;financial-sentence polarity (mean 92 chars)&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;33.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Six systems, two conditions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero-shot (0 labels)&lt;/strong&gt;: Jev, a local 26B MoE (Gemma 4 26B-A4B, Q4_K_M, resident on the same mini-PC), SemIf (Qwen3.5-4B), GLiClass multilang-mini (a GLiNER-family classifier), Laya (typed decisions).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trained (250 labels)&lt;/strong&gt;: &lt;code&gt;sbintuitions/modernbert-ja-310m&lt;/code&gt; with a classification head, and a char n-gram + logistic regression baseline.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The trained numbers are &lt;strong&gt;5-fold cross-validated out-of-fold predictions&lt;/strong&gt;: train on 200, predict the held-out 50, five times, so all 250 reported predictions come from a model that never saw that row. That is the same protocol I use for the linear baseline, which means the trained row and the baseline row are directly comparable.&lt;/p&gt;

&lt;p&gt;Everything is compared on &lt;strong&gt;identical row ids&lt;/strong&gt; with &lt;strong&gt;McNemar's test&lt;/strong&gt;. This matters: at n=250 the independent-confidence interval is about ±6 points, so "0.888 vs 0.944" cannot be called a difference without a paired test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;th&gt;Condition&lt;/th&gt;
&lt;th&gt;livedoor (9)&lt;/th&gt;
&lt;th&gt;rakuten (2)&lt;/th&gt;
&lt;th&gt;chabsa (3)&lt;/th&gt;
&lt;th&gt;p50 latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ModernBERT-ja-310m&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;trained, 250 labels&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;88.8%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;92.8%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;75.2%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.10–0.45s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jev 1.13.0&lt;/td&gt;
&lt;td&gt;zero-shot&lt;/td&gt;
&lt;td&gt;76.8%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;94.4%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;74.0%&lt;/td&gt;
&lt;td&gt;1.9–2.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 4 26B-A4B (local)&lt;/td&gt;
&lt;td&gt;zero-shot&lt;/td&gt;
&lt;td&gt;65.2%&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;94.4%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;75.2%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.24s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SemIf (Qwen3.5-4B)&lt;/td&gt;
&lt;td&gt;zero-shot&lt;/td&gt;
&lt;td&gt;68.8%&lt;/td&gt;
&lt;td&gt;92.0%&lt;/td&gt;
&lt;td&gt;64.0%&lt;/td&gt;
&lt;td&gt;1.3–6.8s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLiClass multilang-mini&lt;/td&gt;
&lt;td&gt;zero-shot&lt;/td&gt;
&lt;td&gt;62.8%&lt;/td&gt;
&lt;td&gt;87.2%&lt;/td&gt;
&lt;td&gt;44.8%&lt;/td&gt;
&lt;td&gt;0.04–0.42s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Laya (typed decisions)&lt;/td&gt;
&lt;td&gt;zero-shot&lt;/td&gt;
&lt;td&gt;29.2%&lt;/td&gt;
&lt;td&gt;60.0%&lt;/td&gt;
&lt;td&gt;40.0%&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;char n-gram + LogReg&lt;/td&gt;
&lt;td&gt;trained, 250 labels&lt;/td&gt;
&lt;td&gt;88.4%&lt;/td&gt;
&lt;td&gt;73.6%&lt;/td&gt;
&lt;td&gt;64.0%&lt;/td&gt;
&lt;td&gt;~0s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;McNemar, same ids:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Comparison&lt;/th&gt;
&lt;th&gt;livedoor&lt;/th&gt;
&lt;th&gt;rakuten&lt;/th&gt;
&lt;th&gt;chabsa&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;trained encoder − Jev&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+12.0pt (p=0.00007)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;−1.6pt (p=0.50)&lt;/td&gt;
&lt;td&gt;+1.2pt (p=0.83)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;trained encoder − local 26B MoE&lt;/td&gt;
&lt;td&gt;+23.6pt (p&amp;lt;1e-15)&lt;/td&gt;
&lt;td&gt;−1.6pt (p=0.45)&lt;/td&gt;
&lt;td&gt;±0.0pt (p=1.00)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;trained encoder − SemIf&lt;/td&gt;
&lt;td&gt;+20.0pt (p&amp;lt;1e-15)&lt;/td&gt;
&lt;td&gt;+0.8pt (p=0.83)&lt;/td&gt;
&lt;td&gt;+11.2pt (p=0.007)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;trained encoder − GLiClass (zero-shot)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+26.0pt (p&amp;lt;1e-15)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;+5.6pt (p=0.016)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+30.4pt (p&amp;lt;1e-15)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;trained encoder − char n-gram LogReg&lt;/td&gt;
&lt;td&gt;+0.4pt&lt;/td&gt;
&lt;td&gt;+19.2pt&lt;/td&gt;
&lt;td&gt;+11.2pt&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Finding 1: the same family wins or loses depending on whether you trained it
&lt;/h2&gt;

&lt;p&gt;This is the part I did not expect, and it is the most useful thing in the table.&lt;/p&gt;

&lt;p&gt;GLiClass is a GLiNER-family classifier — same lineage as the models currently being pitched as open-source Jev alternatives. Zero-shot, it scored &lt;strong&gt;62.8 / 87.2 / 44.8&lt;/strong&gt;: it lost to Jev on all three tasks, and on the 3-class polarity task it was barely above chance (44.8% against 33.3%).&lt;/p&gt;

&lt;p&gt;Trained on 250 of my labels, the same generation of model scored &lt;strong&gt;88.8 / 92.8 / 75.2&lt;/strong&gt;. The gap between zero-shot and trained is &lt;strong&gt;+26.0 / +5.6 / +30.4 points&lt;/strong&gt;, all significant.&lt;/p&gt;

&lt;p&gt;So "is there an open-source model that replaces Jev?" was the wrong question. The models are not the variable — &lt;strong&gt;the labels are&lt;/strong&gt;. A 310M encoder with 250 labels beats a decision API on one task and ties it on two; the same architecture with zero labels loses to everything.&lt;/p&gt;

&lt;p&gt;I also paid for this lesson twice. Before this experiment I ran a proper evaluation of the two models most often named as Jev replacements — SemIf and Laya — on these same rows. &lt;strong&gt;Both failed to replace anything.&lt;/strong&gt; Laya scored 29.2% on a 9-class task (chance 11.1%) with a median confidence of 0.029, i.e. it was visibly not deciding. Its zero-shot English number in its own documentation is 0.362, so this is not a Japanese-specific defect — it is a zero-shot defect. SemIf was closer but lost on all three tasks, two of them significantly.&lt;/p&gt;

&lt;p&gt;Independent benchmarks showed the same thing. On a frozen-question benchmark, Laya's "hard" score was 34.1% while its model card implied a win over Jev, because the model had been fine-tuned on that benchmark's training split. Self-reported comparisons of fine-tuned models are not evidence about zero-shot behaviour.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 2: training pays on topic tasks and does not on meaning tasks
&lt;/h2&gt;

&lt;p&gt;Look at where the 12-point win came from and where it did not.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;livedoor (9-class topic, 1,174 chars)&lt;/strong&gt;: trained &lt;strong&gt;88.8%&lt;/strong&gt; vs Jev 76.8%. The labels are in the text — if you have a few hundred examples, "what topic is this" is a vocabulary problem, and a trained encoder learns the vocabulary. It also beat the char n-gram baseline by a hair (+0.4pt), which tells you the &lt;em&gt;topic signal itself&lt;/em&gt; was already largely lexical.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;rakuten (2-class polarity, 138 chars)&lt;/strong&gt;: trained 92.8% vs Jev 94.4% — a statistical tie (p=0.50). Same for the local 26B MoE (p=0.45). Adding data does not fix this, because the problem is not "I don't know the vocabulary", it is "I need to judge sentiment".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;chabsa (3-class financial polarity, 92 chars)&lt;/strong&gt;: trained 75.2%, Jev 74.0%, local MoE 75.2%. A three-way tie. On this task &lt;strong&gt;no one is good&lt;/strong&gt; — 74–75% is the ceiling for all three approaches, and that is the real finding. If you need better here, you need a third stage (a stronger model on the low-confidence tail), not a different single model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The practical rule I now use: &lt;strong&gt;if the label is visible in the words, train something small; if the label is a judgement about the words, use a decision API; if both plateau around 75%, stop shopping for models and fix the data or add an escalation stage.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 3: the trained encoder is dramatically cheaper to run
&lt;/h2&gt;

&lt;p&gt;Latency, single-threaded, same machine:&lt;/p&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;p50 per item&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;trained encoder (310M, CPU)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.10–0.45s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jev&lt;/td&gt;
&lt;td&gt;1.9–2.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;local 26B MoE&lt;/td&gt;
&lt;td&gt;1.24s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SemIf (4B)&lt;/td&gt;
&lt;td&gt;1.3–6.8s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That is &lt;strong&gt;4–20× faster than the API&lt;/strong&gt;, with zero marginal cost, no network dependency, and no data leaving the machine. The confidence score is a softmax you can calibrate yourself rather than a vendor's number.&lt;/p&gt;

&lt;p&gt;This is the part that changes architecture. Once the local model is both faster and not worse, the API becomes the &lt;em&gt;fallback&lt;/em&gt; rather than the primary path: trained encoder first, Jev only on the low-confidence tail, the novel-looking rows, and as a drift detector.&lt;/p&gt;

&lt;h2&gt;
  
  
  Because you will ask: can I train it on the GPU in the box?
&lt;/h2&gt;

&lt;p&gt;I tried that too, on the same mini-PC (Ryzen 9 7940HS, Radeon 780M iGPU, &lt;code&gt;gfx1103&lt;/code&gt;). Short answer: &lt;strong&gt;it works, and it is not worth switching to.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Getting PyTorch onto that iGPU is now possible through AMD's device-specific wheels, but there are three traps in a row:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installing with the AMD index as the only index makes pip fail to resolve ordinary dependencies, because you have just replaced PyPI.&lt;/li&gt;
&lt;li&gt;Adding PyPI back as an extra index lets &lt;strong&gt;PyPI's newer torch win the version comparison&lt;/strong&gt; — you silently get a CUDA build and a pile of &lt;code&gt;nvidia-*&lt;/code&gt; packages.&lt;/li&gt;
&lt;li&gt;The nightly index's newest torch requires a &lt;code&gt;rocm-bootstrap&lt;/code&gt; version that the same index does not ship.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once pinned properly, the iGPU is real: &lt;code&gt;torch 2.8.0+rocm7.14.0a20260519&lt;/code&gt;, &lt;code&gt;arch=gfx1103&lt;/code&gt;, fp16 matmul fine. The microbenchmark is impressive — &lt;strong&gt;1.21 s/step against 16.41 s/step on CPU, 13.6×&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The real workload does not deliver that:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task (5-fold CV, 310M encoder)&lt;/th&gt;
&lt;th&gt;CPU fp32&lt;/th&gt;
&lt;th&gt;iGPU fp16&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;long documents (512 tokens)&lt;/td&gt;
&lt;td&gt;112 min&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;44.5 min (2.5×)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;short sentences&lt;/td&gt;
&lt;td&gt;21 min&lt;/td&gt;
&lt;td&gt;23 min (&lt;strong&gt;0.9×&lt;/strong&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two reasons the microbenchmark lies. Short sequences leave the GPU idle while the CPU is comparatively efficient at small matrices, and per-fold fixed costs (model load, tokenisation, one-at-a-time evaluation) can exceed the training itself. Only long-document training showed a real gain.&lt;/p&gt;

&lt;p&gt;And it is not stable by default. Roughly every few minutes the driver resets the GPU — &lt;code&gt;amdgpu: ring gfx_0.0.0 timeout&lt;/code&gt; → &lt;code&gt;GPU reset(8)&lt;/code&gt; → then the process dies with &lt;code&gt;HIP error: unspecified launch failure&lt;/code&gt;. The GPU edge temperature was 49°C, so this is a driver problem, not a thermal one. Serialising kernel dispatches and pinning the performance level plus running one fold per process with retries did get a full 5-fold run through cleanly, but fp16 also cost 2.8–4.4 points of accuracy (not significant at n=250, but negative on both tasks).&lt;/p&gt;

&lt;p&gt;So: CPU for training, GPU for what already works — Vulkan inference. If you are training on your own private data, a documented 30 minutes per dataset on CPU is better than a 2.5× speedup that crashes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the labels come from is the whole game
&lt;/h2&gt;

&lt;p&gt;If trained beats zero-shot this decisively, the obvious move is to generate labels with Jev and train on those. That works, with one trap I want to be explicit about, because it is the reason I did not just ship the first version:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If Jev alone produced the labels, Jev's accuracy is the ceiling of the student.&lt;/strong&gt; You are teaching a model to reproduce another model's mistakes. The fix is to spend the labels where they matter: run Jev over everything, then take the &lt;strong&gt;low-confidence&lt;/strong&gt; rows and have a stronger model or a human relabel them. That is where the ceiling comes off, and it is also where the marginal cost is, because high-confidence rows can be accepted as-is.&lt;/p&gt;

&lt;p&gt;At 250 labels the trained encoder is already at parity with Jev on the tasks Jev is strong at, and ahead where Jev is weak. Labels are the lever, and the second and third hundred labels are worth more than the first.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I run now
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No labels, short text, judge-shaped task&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Jev&lt;/strong&gt; (74–94% zero-shot, no setup)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No labels, must stay offline or in-machine&lt;/td&gt;
&lt;td&gt;local 26B MoE (ties Jev on both polarity tasks)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A few hundred labels, topic-shaped task&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;train a small encoder&lt;/strong&gt; (beats Jev by 12pt, 4–20× faster)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A few hundred labels, meaning task&lt;/td&gt;
&lt;td&gt;trained encoder, but accept parity with Jev&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Everything plateaus at ~75%&lt;/td&gt;
&lt;td&gt;stop model-shopping; escalate the low-confidence tail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Labels to be generated&lt;/td&gt;
&lt;td&gt;Jev for volume, a stronger model or a human on the low-confidence rows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The trained numbers are 5-fold CV over 250 rows, so 200 rows of training data per fold. That is a floor, not a ceiling — more labels should widen the topic-task margin.&lt;/li&gt;
&lt;li&gt;livedoor was truncated at 512 tokens, so the longest documents are not fully represented.&lt;/li&gt;
&lt;li&gt;The trained side reports raw argmax; I have not temperature-scaled it, so its confidence is not yet comparable to Jev's for thresholding.&lt;/li&gt;
&lt;li&gt;The char n-gram baseline is a single configuration. It may not be the strongest non-neural baseline available.&lt;/li&gt;
&lt;li&gt;The iGPU accuracy comparison is at n=250 per task, which is not enough to make a 3-point effect significant. "Not significant" is not "no effect."&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Takeaway&lt;/strong&gt;: Zero-shot and trained are different products, not better and worse versions of the same one. If you have no labels, a decision API is the only thing that works today. If you have a few hundred labels and the label is visible in the text, a 310M encoder will beat it, cost nothing to run, and answer in a tenth of a second. Measure your task's shape before you pick a winner — and be suspicious of any "open-source Jev replacement" benchmark where the model was trained on the benchmark.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The API used here&lt;/strong&gt;: TypeSafe AI's Jev (System One, &lt;code&gt;jev-latest&lt;/code&gt;) — text plus a list of questions in, a 0–1 score per question out, own confidence included, roughly 32k of context, $0.042 per 1M input tokens with output free. Both controlled and randomised question sets were frozen and committed before the runs, and every comparison above is on identical row ids.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you have run the same shape of experiment on your own data, I want to know where a trained small model beat the API and where it did not — especially on tasks where both plateaued.&lt;/p&gt;

&lt;h3&gt;
  
  
  Related reading
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://agentjournal.dev/blog/llm-judge-vs-feature-extraction/" rel="noopener noreferrer"&gt;Jev: one judge call, or twelve dimension scores?&lt;/a&gt; — the same rows, attacking the &lt;em&gt;other&lt;/em&gt; axis: decomposing one judgment into scored dimensions.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://agentjournal.dev/blog/embedding-models-japanese/" rel="noopener noreferrer"&gt;Japanese embeddings, measured&lt;/a&gt; — the retrieval side of the same question.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://agentjournal.dev/blog/self-hosted-llm-economics/" rel="noopener noreferrer"&gt;Self-hosted LLM economics&lt;/a&gt; — when running the model yourself actually pays.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>llm</category>
      <category>ai</category>
      <category>jev</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
