<?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/" xmlns:media="http://search.yahoo.com/mrss/">
  <channel>
    <title>StoatFlow blog</title>
    <link>https://stoatflow.io/blog</link>
    <description>Engineering deep dives and product announcements from StoatFlow — stateful stream processing for Kafka.</description>
    <language>en</language>
    <lastBuildDate>Tue, 15 Sep 2026 00:00:00 GMT</lastBuildDate>
    <atom:link href="https://stoatflow.io/blog/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Three clocks: stream time, Flink watermarks, and the one watermark StoatFlow computes</title>
      <link>https://stoatflow.io/blog/three-clocks-stream-time-vs-watermarks</link>
      <guid isPermaLink="true">https://stoatflow.io/blog/three-clocks-stream-time-vs-watermarks</guid>
      <pubDate>Tue, 15 Sep 2026 00:00:00 GMT</pubDate>
      <description>Kafka Streams, Flink and StoatFlow answer &quot;is this window done?&quot; with three different clocks. StoatFlow runs two of them: it accepts a late record the way Kafka Streams does, on the operator&apos;s stream time, and closes the window the way Flink does, on a single global watermark. Part 1 of a four-part series on event time — including two corrections to a claim our own compatibility matrix made about grace.</description>
      <dc:creator>Hartmut Armbruster</dc:creator>
      <category>Deep Dive</category>
      <enclosure url="https://stoatflow.io/assets/blog/og/three-clocks-stream-time-vs-watermarks.png" type="image/png" length="89202" />
      <media:content url="https://stoatflow.io/assets/blog/og/three-clocks-stream-time-vs-watermarks.png" medium="image" type="image/png" />
    </item>
    <item>
      <title>Scheduled sources: a topology source that isn&apos;t a topic</title>
      <link>https://stoatflow.io/blog/scheduled-sources</link>
      <guid isPermaLink="true">https://stoatflow.io/blog/scheduled-sources</guid>
      <pubDate>Sat, 29 Aug 2026 00:00:00 GMT</pubDate>
      <description>Every source in a Kafka Streams topology is a topic. The calendar and your own state — a snapshot across keys, a sweep over what you hold — needed a job outside the topology or a punctuator bent into a source. StoatFlow&apos;s scheduled sources run your code on a schedule and turn what it forwards into an ordinary KStream: keyed, processed in parallel on the lanes, committed with the stream. What that opens up, how it works, two verified examples, and the catch — the emitter&apos;s view is read-only, a run does not yield to live traffic on its own, and a read from outside the engine is not in the transaction.</description>
      <dc:creator>Hartmut Armbruster</dc:creator>
      <category>Engineering</category>
      <enclosure url="https://stoatflow.io/assets/blog/og/scheduled-sources.png" type="image/png" length="73675" />
      <media:content url="https://stoatflow.io/assets/blog/og/scheduled-sources.png" medium="image" type="image/png" />
    </item>
    <item>
      <title>Cutting CI from 50 minutes to 16: choosing a runner platform</title>
      <link>https://stoatflow.io/blog/ci-runner-platform-case-study</link>
      <guid isPermaLink="true">https://stoatflow.io/blog/ci-runner-platform-case-study</guid>
      <pubDate>Wed, 26 Aug 2026 00:00:00 GMT</pubDate>
      <description>Our CI p90 hit 54 minutes and the constraint was RAM, not CPU — a faster runner was the wrong instinct. We built a self-hosted Hetzner fleet, validated it, chose Blacksmith on a head-to-head benchmark, then took another eight minutes off by deleting four lines of YAML. Four weeks on: the speed held, the cost model did not.</description>
      <dc:creator>Hartmut Armbruster</dc:creator>
      <category>Engineering</category>
      <enclosure url="https://stoatflow.io/assets/blog/og/ci-runner-platform-case-study.png" type="image/png" length="65113" />
      <media:content url="https://stoatflow.io/assets/blog/og/ci-runner-platform-case-study.png" medium="image" type="image/png" />
    </item>
    <item>
      <title>From first alpha to release candidate: the road to StoatFlow 1.0.0</title>
      <link>https://stoatflow.io/blog/road-to-1-0-0</link>
      <guid isPermaLink="true">https://stoatflow.io/blog/road-to-1-0-0</guid>
      <pubDate>Tue, 04 Aug 2026 00:00:00 GMT</pubDate>
      <description>StoatFlow 1.0.0-rc.1 is cut and the feature set is frozen — what separates the candidate from GA is proof, not features. The twelve weeks from the first alpha: 26 releases, 1,086 commits, a compatibility matrix grown from 417 to 619 tracked entries, three full-codebase review rounds — and the framework integration that kept us honest.</description>
      <dc:creator>Hartmut Armbruster</dc:creator>
      <category>Announcement</category>
      <enclosure url="https://stoatflow.io/assets/blog/og/road-to-1-0-0.png" type="image/png" length="69656" />
      <media:content url="https://stoatflow.io/assets/blog/og/road-to-1-0-0.png" medium="image" type="image/png" />
    </item>
    <item>
      <title>Three gates, one transaction: error handling in StoatFlow</title>
      <link>https://stoatflow.io/blog/error-handling-three-gates-one-transaction</link>
      <guid isPermaLink="true">https://stoatflow.io/blog/error-handling-three-gates-one-transaction</guid>
      <pubDate>Mon, 27 Jul 2026 00:00:00 GMT</pubDate>
      <description>A record can fail on the way in, in the middle, or on the way out. StoatFlow gives every gate the same verdicts — continue, fail, or dead-letter — and settles them all inside the exactly-once transaction: DLQ records commit on the same barrier as your output, and what cannot be handled kills the epoch, never the guarantee. The full model, from one bad record to a Kubernetes restart.</description>
      <dc:creator>Hartmut Armbruster</dc:creator>
      <category>Deep Dive</category>
      <enclosure url="https://stoatflow.io/assets/blog/og/error-handling-three-gates-one-transaction.png" type="image/png" length="101951" />
      <media:content url="https://stoatflow.io/assets/blog/og/error-handling-three-gates-one-transaction.png" medium="image" type="image/png" />
    </item>
    <item>
      <title>llms.txt for StoatFlow: docs your AI agent can fetch</title>
      <link>https://stoatflow.io/blog/llms-txt-machine-readable-docs</link>
      <guid isPermaLink="true">https://stoatflow.io/blog/llms-txt-machine-readable-docs</guid>
      <pubDate>Sun, 26 Jul 2026 00:00:00 GMT</pubDate>
      <description>The StoatFlow documentation is now published as llms.txt, llms-full.txt, and raw markdown — the retrieval-side complement to the AI Assistant Skills pack. Nothing to install.</description>
      <dc:creator>Hartmut Armbruster</dc:creator>
      <category>Announcement</category>
      <enclosure url="https://stoatflow.io/assets/blog/og/llms-txt-machine-readable-docs.png" type="image/png" length="65174" />
      <media:content url="https://stoatflow.io/assets/blog/og/llms-txt-machine-readable-docs.png" medium="image" type="image/png" />
    </item>
    <item>
      <title>StoatFlow AI Assistant Skills: correct StoatFlow, not hallucinated Kafka Streams</title>
      <link>https://stoatflow.io/blog/ai-assistant-skills</link>
      <guid isPermaLink="true">https://stoatflow.io/blog/ai-assistant-skills</guid>
      <pubDate>Fri, 24 Jul 2026 00:00:00 GMT</pubDate>
      <description>A free, open skills pack that makes AI coding assistants write correct StoatFlow instead of hallucinated Kafka Streams.</description>
      <dc:creator>Hartmut Armbruster</dc:creator>
      <category>Announcement</category>
      <enclosure url="https://stoatflow.io/assets/blog/og/ai-assistant-skills.png" type="image/png" length="73271" />
      <media:content url="https://stoatflow.io/assets/blog/og/ai-assistant-skills.png" medium="image" type="image/png" />
    </item>
    <item>
      <title>Internal consistency on Kafka: emitting a correct answer at every commit</title>
      <link>https://stoatflow.io/blog/internal-consistency</link>
      <guid isPermaLink="true">https://stoatflow.io/blog/internal-consistency</guid>
      <pubDate>Mon, 13 Jul 2026 00:00:00 GMT</pubDate>
      <description>Money can only be moved, never created — so a stream that tracks balances should read total = 0 at every consistent cut. The Flink Table API gets it right 0.035% of the time; our Kafka Streams twin sends total to −1,619 … +1,792. StoatFlow holds it at exactly 0, at every one of its committed cuts. Here is why, and the measured proof.</description>
      <dc:creator>Hartmut Armbruster</dc:creator>
      <category>Deep Dive</category>
      <enclosure url="https://stoatflow.io/assets/blog/og/internal-consistency.png" type="image/png" length="100813" />
      <media:content url="https://stoatflow.io/assets/blog/og/internal-consistency.png" medium="image" type="image/png" />
    </item>
    <item>
      <title>In-place engine restart: the primitive behind multi-standby HA</title>
      <link>https://stoatflow.io/blog/in-place-restart-multi-standby</link>
      <guid isPermaLink="true">https://stoatflow.io/blog/in-place-restart-multi-standby</guid>
      <pubDate>Thu, 02 Jul 2026 00:00:00 GMT</pubDate>
      <description>The failover-testing post ended on an exploratory idea — rebuild the processing engine in the same process, no JVM exit — and a constraint: stay at two standbys. Both have moved. The in-place restart shipped, and a lag-aware leader election shipped on top, so a StoatFlow HA cluster now runs one active and any number of warm standbys, scaling past two elects exactly one successor instead of crash-looping, and a graceful role swap no longer bounces the pod.</description>
      <dc:creator>Hartmut Armbruster</dc:creator>
      <category>Engineering</category>
      <enclosure url="https://stoatflow.io/assets/blog/og/in-place-restart-multi-standby.png" type="image/png" length="61318" />
      <media:content url="https://stoatflow.io/assets/blog/og/in-place-restart-multi-standby.png" medium="image" type="image/png" />
    </item>
    <item>
      <title>How we obfuscate StoatFlow — and what the public-API boundary taught us</title>
      <link>https://stoatflow.io/blog/obfuscation-and-the-public-api-boundary</link>
      <guid isPermaLink="true">https://stoatflow.io/blog/obfuscation-and-the-public-api-boundary</guid>
      <pubDate>Wed, 01 Jul 2026 00:00:00 GMT</pubDate>
      <description>A drop-in Kafka Streams DSL has to stay byte-stable while the engine beneath it stays hidden. Those two goals pull apart — and every interesting bug we hit while obfuscating StoatFlow lived on the seam between them.</description>
      <dc:creator>Hartmut Armbruster</dc:creator>
      <category>Deep Dive</category>
      <enclosure url="https://stoatflow.io/assets/blog/og/obfuscation-and-the-public-api-boundary.png" type="image/png" length="133426" />
      <media:content url="https://stoatflow.io/assets/blog/og/obfuscation-and-the-public-api-boundary.png" medium="image" type="image/png" />
    </item>
    <item>
      <title>Measuring StoatFlow failover: four scenarios, from the logs</title>
      <link>https://stoatflow.io/blog/ha-failover-testing</link>
      <guid isPermaLink="true">https://stoatflow.io/blog/ha-failover-testing</guid>
      <pubDate>Sat, 27 Jun 2026 00:00:00 GMT</pubDate>
      <description>The obvious way to test HA failover — kubectl delete pod — is a graceful SIGTERM handoff, not a crash. Here are the four real failover scenarios, the millisecond timing measured from pod logs (because a 10-second Prometheus scrape cannot resolve a one-second event), and what the numbers revealed: a single JVM crash recovers in place without failing over, and stop-the-world is not the latency blip.</description>
      <dc:creator>Hartmut Armbruster</dc:creator>
      <category>Engineering</category>
      <enclosure url="https://stoatflow.io/assets/blog/og/ha-failover-testing.png" type="image/png" length="60289" />
      <media:content url="https://stoatflow.io/assets/blog/og/ha-failover-testing.png" medium="image" type="image/png" />
    </item>
    <item>
      <title>Hot standby for StoatFlow: failover in seconds, not a cold restore</title>
      <link>https://stoatflow.io/blog/hot-standby-high-availability</link>
      <guid isPermaLink="true">https://stoatflow.io/blog/hot-standby-high-availability</guid>
      <pubDate>Wed, 17 Jun 2026 00:00:00 GMT</pubDate>
      <description>StoatFlow recovery has always been fast restart — but restart time scales with state. Hot standby is an opt-in active/passive pair that fails over in seconds regardless of state size, with exactly-once preserved across the handoff. Redundancy, not scale-out.</description>
      <dc:creator>Hartmut Armbruster</dc:creator>
      <category>Announcement</category>
      <enclosure url="https://stoatflow.io/assets/blog/og/hot-standby-high-availability.png" type="image/png" length="69054" />
      <media:content url="https://stoatflow.io/assets/blog/og/hot-standby-high-availability.png" medium="image" type="image/png" />
    </item>
    <item>
      <title>KIP-1271: record headers in state stores, and the cost of a value format</title>
      <link>https://stoatflow.io/blog/kip-1271-record-headers-in-state-stores</link>
      <guid isPermaLink="true">https://stoatflow.io/blog/kip-1271-record-headers-in-state-stores</guid>
      <pubDate>Sun, 14 Jun 2026 00:00:00 GMT</pubDate>
      <description>KIP-1271/1285 let a state store keep a record&apos;s headers next to its value — shipped in Apache Kafka 4.3.0. What the KIP is, where Kafka Streams has got to (and what is still in flight), how StoatFlow 1.0.0 compares — and why a one-varint format change becomes an engine-wide one.</description>
      <dc:creator>Hartmut Armbruster</dc:creator>
      <category>Deep Dive</category>
      <enclosure url="https://stoatflow.io/assets/blog/og/kip-1271-record-headers-in-state-stores.png" type="image/png" length="96301" />
      <media:content url="https://stoatflow.io/assets/blog/og/kip-1271-record-headers-in-state-stores.png" medium="image" type="image/png" />
    </item>
    <item>
      <title>KIP-1035: Why Kafka Streams 4.3 lets state stores own their offsets</title>
      <link>https://stoatflow.io/blog/kip-1035-state-store-managed-offsets</link>
      <guid isPermaLink="true">https://stoatflow.io/blog/kip-1035-state-store-managed-offsets</guid>
      <pubDate>Sat, 06 Jun 2026 00:00:00 GMT</pubDate>
      <description>Kafka Streams kept each task&apos;s changelog offsets in a separate checkpoint file that could drift from the state it described. KIP-1035, in Kafka 4.3, moves them inside the state store — atomic with the data, and the keystone for transactional state stores.</description>
      <dc:creator>Hartmut Armbruster</dc:creator>
      <category>Deep Dive</category>
      <enclosure url="https://stoatflow.io/assets/blog/og/kip-1035-state-store-managed-offsets.png" type="image/png" length="98453" />
      <media:content url="https://stoatflow.io/assets/blog/og/kip-1035-state-store-managed-offsets.png" medium="image" type="image/png" />
    </item>
    <item>
      <title>Compiling StoatFlow to a GraalVM native image: G1, PGO, and why JNI beats FFM under AOT</title>
      <link>https://stoatflow.io/blog/native-image-g1-pgo-jni-vs-ffm</link>
      <guid isPermaLink="true">https://stoatflow.io/blog/native-image-g1-pgo-jni-vs-ffm</guid>
      <pubDate>Sat, 06 Jun 2026 00:00:00 GMT</pubDate>
      <description>Native image is viable for a throughput-first stream processor — but only with Oracle G1 + PGO. The wrong turn (we blamed FFM; it was the GC) and the reversal (FFM is faster on the JVM, slower under AOT) are the most interesting parts.</description>
      <dc:creator>Hartmut Armbruster</dc:creator>
      <category>Engineering</category>
      <enclosure url="https://stoatflow.io/assets/blog/og/native-image-g1-pgo-jni-vs-ffm.png" type="image/png" length="84466" />
      <media:content url="https://stoatflow.io/assets/blog/og/native-image-g1-pgo-jni-vs-ffm.png" medium="image" type="image/png" />
    </item>
    <item>
      <title>StoatFlow: Kafka Streams compatible engine built to scale up — not out</title>
      <link>https://stoatflow.io/blog/introducing-stoatflow</link>
      <guid isPermaLink="true">https://stoatflow.io/blog/introducing-stoatflow</guid>
      <pubDate>Sun, 17 May 2026 00:00:00 GMT</pubDate>
      <description>The first alpha is here. Kafka Streams DSL on a single-replica runtime with virtual-thread parallelism — measurably less CPU, memory, and latency on the same hardware.</description>
      <dc:creator>Hartmut Armbruster</dc:creator>
      <category>Announcement</category>
      <enclosure url="https://stoatflow.io/assets/blog/og/introducing-stoatflow.png" type="image/png" length="146805" />
      <media:content url="https://stoatflow.io/assets/blog/og/introducing-stoatflow.png" medium="image" type="image/png" />
    </item>
  </channel>
</rss>
