<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Halyx blog</title>
  <link href="https://halyx.dev/blog/feed.xml" rel="self"/>
  <link href="https://halyx.dev/blog/"/>
  <id>https://halyx.dev/blog/</id>
  <updated>2026-11-02T00:00:00.000Z</updated>
  <author><name>Kamran Zafar</name></author>
  <entry>
    <title>Halyx 1.1.0</title>
    <link href="https://halyx.dev/blog/halyx-1-1-0.html"/>
    <id>https://halyx.dev/blog/halyx-1-1-0.html</id>
    <updated>2026-11-02T00:00:00.000Z</updated>
    <author><name>Kamran Zafar</name></author>
    <summary>Function invocation on Lambda, Azure Functions and Cloud Run functions, keys and passwords that rotate without a restart, and Kotlin coroutines. Compatible with 1.0.</summary>
    <content type="html">&lt;p&gt;Halyx 1.1.0 adds a ninth service, function invocation, and reads keys and passwords from a parameter store as
they're used, so rotating one no longer means restarting. It also brings Kotlin coroutines to listeners and
repositories. Code written for 1.0 keeps compiling and running unchanged.&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;btn-row&quot;&gt;&lt;a class=&quot;btn btn-secondary&quot; href=&quot;https://github.com/halyx-dev/halyx/releases/tag/v1.1.0&quot; rel=&quot;noopener&quot;&gt;Release on GitHub&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;function-invocation&quot;&gt;Function invocation&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-1-0.html#function-invocation&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Issue &lt;a href=&quot;https://github.com/halyx-dev/halyx/issues/22&quot; rel=&quot;noopener&quot; class=&quot;external&quot;&gt;#22&lt;/a&gt;. A &lt;code&gt;FunctionInvoker&lt;/code&gt; calls an AWS Lambda function, an
Azure Function or a Cloud Run function by name, with each cloud's login: IAM on Lambda, an Entra ID token or a
function key on Azure, and a Google ID token on Cloud Run.&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;Java&lt;/span&gt;&lt;pre&gt;&lt;code class=&quot;hljs language-java&quot;&gt;&lt;span class=&quot;hljs-type&quot;&gt;FunctionInvoker&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;resize&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; halyx.provider(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;images&amp;quot;&lt;/span&gt;).functionInvoker(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;resize-image&amp;quot;&lt;/span&gt;);

&lt;span class=&quot;hljs-type&quot;&gt;FunctionResponse&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; resize.invoke(FunctionRequest.builder(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;{\&amp;quot;key\&amp;quot;:\&amp;quot;a.png\&amp;quot;}&amp;quot;&lt;/span&gt;)
        .header(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;X-Tenant&amp;quot;&lt;/span&gt;, tenant)
        .build());
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The behaviour is the same on all three clouds:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A function that throws raises &lt;code&gt;UnavailableException&lt;/code&gt;, and a missing one &lt;code&gt;NotFoundException&lt;/code&gt;. What the function
reported is kept whole as a &lt;code&gt;FunctionError&lt;/code&gt; cause: Lambda's error message, type and payload, or an HTTP function's
status and body (&lt;a href=&quot;https://github.com/halyx-dev/halyx/issues/31&quot; rel=&quot;noopener&quot; class=&quot;external&quot;&gt;#31&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;An invocation runs the function, so it's retried only when the function is configured &lt;code&gt;idempotent&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Requests over a cloud's payload or header limit are refused before they're sent.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;trigger&lt;/code&gt; starts a Lambda function without waiting. There are &lt;code&gt;async()&lt;/code&gt; and reactive views, trace context
carried into the function, and &lt;code&gt;halyx.functions&lt;/code&gt; in Spring Boot.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A function conformance kit checks every provider: in memory, on Floci's Lambda, the Azure Functions host and Google's
Functions Framework, and on real Lambda, Azure Functions and Cloud Run functions. See the
&lt;a href=&quot;https://halyx.dev/docs/api/functions.html&quot;&gt;functions guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;keys-and-passwords-that-rotate&quot;&gt;Keys and passwords that rotate&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-1-0.html#keys-and-passwords-that-rotate&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Issue &lt;a href=&quot;https://github.com/halyx-dev/halyx/issues/26&quot; rel=&quot;noopener&quot; class=&quot;external&quot;&gt;#26&lt;/a&gt;. Logging in with IAM or Entra ID is still the best way, but
some services take a key or a password. Halyx now reads them from a parameter store as they're used, so a rotation
takes effect without a restart:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Azure function keys, on every call.&lt;/li&gt;
&lt;li&gt;Cosmos DB account keys and Event Grid access keys, every &lt;code&gt;azure.key-refresh-interval&lt;/code&gt; (a minute by default).&lt;/li&gt;
&lt;li&gt;Cache and Kafka passwords, each time a connection logs in, as SQL passwords already were.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In Spring Boot, these are &lt;code&gt;secrets&lt;/code&gt; entries and &lt;code&gt;auth.secret&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;span class=&quot;code-lang&quot;&gt;YAML&lt;/span&gt;&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;halyx:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;providers:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;azure:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;azure&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;properties:&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;azure.cosmos.endpoint:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;https://acme.documents.azure.com:443/&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;secrets:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;property:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;azure.cosmos.key&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;store:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;app-secrets&lt;/span&gt;       &lt;span class=&quot;hljs-comment&quot;&gt;# one of halyx.parameter-stores, on any provider&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;orders-cosmos-key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A missing secret fails the start, naming the entry, and so does a &lt;code&gt;halyx.*&lt;/code&gt; value whose &lt;code&gt;${...}&lt;/code&gt; placeholder didn't
resolve. Before, Spring left the placeholder as written, and it was sent as the key. The store can be on
&lt;a href=&quot;https://halyx.dev/docs/api/parameters.html#a-store-on-another-cloud&quot;&gt;another cloud&lt;/a&gt;: a function on Azure can keep its key in AWS
Secrets Manager. See &lt;a href=&quot;https://halyx.dev/docs/api/parameters.html#keys-for-other-services&quot;&gt;Keys for other services&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;callout callout-note&quot; role=&quot;note&quot;&gt;&lt;p class=&quot;callout-title&quot;&gt;Note&lt;/p&gt;
&lt;p&gt;Service Bus and App Configuration connection strings are still read once, because their SDKs copy the key when the
client is built. For keys that rotate there, log in with Entra ID.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;kotlin-coroutines&quot;&gt;Kotlin coroutines&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-1-0.html#kotlin-coroutines&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Issue &lt;a href=&quot;https://github.com/halyx-dev/halyx/issues/11&quot; rel=&quot;noopener&quot; class=&quot;external&quot;&gt;#11&lt;/a&gt;. A &lt;code&gt;@HalyxListener&lt;/code&gt; can be a &lt;code&gt;suspend fun&lt;/code&gt;, for
subscriptions and stream consumers. Returning acknowledges the message and throwing retries it, as with any listener.
Repositories take &lt;code&gt;suspend&lt;/code&gt; CRUD methods and derived queries, with &lt;code&gt;Flow&lt;/code&gt; results read page by page.&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs language-kotlin&quot;&gt;&lt;span class=&quot;hljs-meta&quot;&gt;@HalyxListener(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;billing&amp;quot;&lt;/span&gt;)&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;onOrder&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(placed: &lt;span class=&quot;hljs-type&quot;&gt;CloudEvent&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt; {
    invoices.put(BlobKey.of(placed.subject().orElseThrow()), render(placed)).awaitSingle()
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Tested on Boot 3.5 (Kotlin 1.9) and Boot 4 (Kotlin 2.3). See
&lt;a href=&quot;https://halyx.dev/docs/spring-boot.html#reactive-applications&quot;&gt;Reactive applications&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;also-in-110&quot;&gt;Also in 1.1.0&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-1-0.html#also-in-110&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API compatibility checks&lt;/strong&gt; (&lt;a href=&quot;https://github.com/halyx-dev/halyx/issues/4&quot; rel=&quot;noopener&quot; class=&quot;external&quot;&gt;#4&lt;/a&gt;): every build compares each
published module with the last release, the provider SPI included, and fails on an incompatible change. 1.1.0 was
checked against 1.0.0.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dependency scanning&lt;/strong&gt;: OSV-Scanner checks every published module and the samples on each change, and weekly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dependencies&lt;/strong&gt;: tested on Spring Boot 4.1.2 and 3.5.16, and on the Google Cloud libraries 26.91.0, with the
OpenTelemetry API held at 1.67.0 for a security fix.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before the release, every real-cloud tier passed on AWS, Azure and Google Cloud, with Spring Boot 3.5 and 4,
including the key rotation tests on Azure. The portable orders service passed on EKS, GKE and AKS. The results are
in &lt;a href=&quot;https://halyx.dev/docs/cloud-testing/index.html&quot;&gt;Cloud testing&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;upgrading&quot;&gt;Upgrading&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-1-0.html#upgrading&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bump the BOM; nothing else changes.&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs language-kotlin&quot;&gt;implementation(platform(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;dev.halyx:halyx-bom:1.1.0&amp;quot;&lt;/span&gt;))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;From 1.1.0, security fixes go to the 1.1 line.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Halyx 1.0.2</title>
    <link href="https://halyx.dev/blog/halyx-1-0-2.html"/>
    <id>https://halyx.dev/blog/halyx-1-0-2.html</id>
    <updated>2026-10-25T00:00:00.000Z</updated>
    <author><name>Kamran Zafar</name></author>
    <summary>Fixes for the JSON cache codec on Spring Boot 4 and for cancelling reactive calls, and a new plain Java quickstart sample.</summary>
    <content type="html">&lt;p&gt;Halyx 1.0.2 fixes the JSON cache codec on Spring Boot 4 and changes how cancelled reactive calls behave. It also adds
a quickstart sample for applications that don't use Spring.&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;btn-row&quot;&gt;&lt;a class=&quot;btn btn-secondary&quot; href=&quot;https://github.com/halyx-dev/halyx/releases/tag/v1.0.2&quot; rel=&quot;noopener&quot;&gt;Release on GitHub&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;fixes&quot;&gt;Fixes&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-0-2.html#fixes&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;json-cache-codec-on-spring-boot-4&quot;&gt;JSON cache codec on Spring Boot 4&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-0-2.html#json-cache-codec-on-spring-boot-4&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Issue &lt;a href=&quot;https://github.com/halyx-dev/halyx/issues/9&quot; rel=&quot;noopener&quot; class=&quot;external&quot;&gt;#9&lt;/a&gt;. With &lt;code&gt;halyx.cache-manager.codec=json&lt;/code&gt;, and with Jackson 2
also on the classpath (&lt;code&gt;halyx-azure&lt;/code&gt; brings it in), the codec used Jackson 2 without Java date/time support. Caching a
value that held an &lt;code&gt;Instant&lt;/code&gt; failed. Jackson 3 is now always preferred when it's present.&lt;/p&gt;
&lt;h3 id=&quot;cancelling-a-reactive-call&quot;&gt;Cancelling a reactive call&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-0-2.html#cancelling-a-reactive-call&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Issue &lt;a href=&quot;https://github.com/halyx-dev/halyx/issues/12&quot; rel=&quot;noopener&quot; class=&quot;external&quot;&gt;#12&lt;/a&gt;. Cancelling a &lt;code&gt;ReactiveHalyx&lt;/code&gt; call that had already
started, with &lt;code&gt;timeout()&lt;/code&gt; for example, interrupted its thread on the default virtual-thread scheduler and on
&lt;code&gt;boundedElastic&lt;/code&gt;. That could abort a blocking SDK call partway through.&lt;/p&gt;
&lt;p&gt;Now a call that has started runs to completion, and its result is dropped, as documented. A call cancelled before
it starts never runs.&lt;/p&gt;
&lt;div class=&quot;callout callout-note&quot; role=&quot;note&quot;&gt;&lt;p class=&quot;callout-title&quot;&gt;Note&lt;/p&gt;
&lt;p&gt;Calls on the non-blocking path are cancelled with the subscription, and the driver may abort the request. The
&lt;a href=&quot;https://halyx.dev/docs/api/reactive.html&quot;&gt;reactive guide&lt;/a&gt; now says so.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;samples&quot;&gt;Samples&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-0-2.html#samples&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;plain-java-quickstart&quot;&gt;Plain Java quickstart&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-0-2.html#plain-java-quickstart&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Issue &lt;a href=&quot;https://github.com/halyx-dev/halyx/issues/2&quot; rel=&quot;noopener&quot; class=&quot;external&quot;&gt;#2&lt;/a&gt;, contributed in
&lt;a href=&quot;https://github.com/halyx-dev/halyx/pull/8&quot; rel=&quot;noopener&quot; class=&quot;external&quot;&gt;#8&lt;/a&gt;.
&lt;a href=&quot;https://github.com/halyx-dev/halyx/tree/main/halyx-samples/plain-java-quickstart&quot; rel=&quot;noopener&quot; class=&quot;external&quot;&gt;&lt;code&gt;halyx-samples/plain-java-quickstart&lt;/code&gt;&lt;/a&gt;
shows object storage, messaging and parameters without Spring. It runs on the in-memory provider by default, or on
a cloud that you pick with system properties. The README and &lt;a href=&quot;https://halyx.dev/docs/getting-started.html&quot;&gt;Getting started&lt;/a&gt; link to it.&lt;/p&gt;
&lt;h2 id=&quot;upgrading&quot;&gt;Upgrading&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-0-2.html#upgrading&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bump the BOM; nothing else changes.&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs language-kotlin&quot;&gt;implementation(platform(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;dev.halyx:halyx-bom:1.0.2&amp;quot;&lt;/span&gt;))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
</content>
  </entry>
  <entry>
    <title>Halyx 1.0.1</title>
    <link href="https://halyx.dev/blog/halyx-1-0-1.html"/>
    <id>https://halyx.dev/blog/halyx-1-0-1.html</id>
    <updated>2026-10-23T10:29:00.000Z</updated>
    <author><name>Kamran Zafar</name></author>
    <summary>A patch release that fixes the AWS, Azure and Google Cloud modules resolving under Gradle with Spring Boot 3.5, and closes reactive blobs on time.</summary>
    <content type="html">&lt;p&gt;Halyx 1.0.1 is a patch release with two fixes. If you build with Gradle on Spring Boot 3.5, you need it.&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;btn-row&quot;&gt;&lt;a class=&quot;btn btn-secondary&quot; href=&quot;https://github.com/halyx-dev/halyx/releases/tag/v1.0.1&quot; rel=&quot;noopener&quot;&gt;Release on GitHub&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;fixes&quot;&gt;Fixes&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-0-1.html#fixes&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;gradle-with-spring-boot-35&quot;&gt;Gradle with Spring Boot 3.5&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-0-1.html#gradle-with-spring-boot-35&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;halyx-aws&lt;/code&gt;, &lt;code&gt;halyx-azure&lt;/code&gt; and &lt;code&gt;halyx-gcp&lt;/code&gt; failed to resolve. &lt;code&gt;halyx-kafka&lt;/code&gt; brought in both the patched
&lt;code&gt;at.yawk.lz4:lz4-java&lt;/code&gt; and the &lt;code&gt;org.lz4:lz4-java&lt;/code&gt; that Boot 3.5's &lt;code&gt;kafka-clients&lt;/code&gt; depends on, and Gradle refuses
two modules that declare the same capability.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;halyx-kafka&lt;/code&gt; now excludes &lt;code&gt;org.lz4:lz4-java&lt;/code&gt;, so only the patched one is on the classpath. That holds for Maven
builds too.&lt;/p&gt;
&lt;h3 id=&quot;reactiveblobstoregetbody-closes-on-time&quot;&gt;&lt;code&gt;ReactiveBlobStore.getBody&lt;/code&gt; closes on time&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-0-1.html#reactiveblobstoregetbody-closes-on-time&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The blob is now closed before the completion or error reaches the subscriber, as documented, rather than just
after it.&lt;/p&gt;
&lt;h2 id=&quot;upgrading&quot;&gt;Upgrading&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-0-1.html#upgrading&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Bump the BOM; nothing else changes.&lt;/p&gt;
&lt;div class=&quot;code-block&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs language-kotlin&quot;&gt;implementation(platform(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;dev.halyx:halyx-bom:1.0.1&amp;quot;&lt;/span&gt;))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
</content>
  </entry>
  <entry>
    <title>Halyx 1.0.0</title>
    <link href="https://halyx.dev/blog/halyx-1-0-0.html"/>
    <id>https://halyx.dev/blog/halyx-1-0-0.html</id>
    <updated>2026-10-23T06:45:00.000Z</updated>
    <author><name>Kamran Zafar</name></author>
    <summary>The first release of Halyx, one Java API for storage, documents, messaging, events, streams, SQL, caches and secrets on AWS, Azure and Google Cloud.</summary>
    <content type="html">&lt;p&gt;Halyx 1.0.0 is out: one Java API for object storage, NoSQL documents, messaging, events, streams, SQL databases,
caches, and parameters and secrets on AWS, Azure and Google Cloud, built over each cloud's own SDKs.&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;btn-row&quot;&gt;&lt;a class=&quot;btn btn-primary&quot; href=&quot;https://halyx.dev/docs/getting-started.html&quot;&gt;Get started&lt;/a&gt;
&lt;a class=&quot;btn btn-secondary&quot; href=&quot;https://github.com/halyx-dev/halyx/releases/tag/v1.0.0&quot; rel=&quot;noopener&quot;&gt;Release on GitHub&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id=&quot;requirements&quot;&gt;Requirements&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-0-0.html#requirements&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Java 21 or later.&lt;/li&gt;
&lt;li&gt;For Spring Boot applications, Spring Boot 3.5 or 4.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;whats-in-it&quot;&gt;What's in it&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-0-0.html#whats-in-it&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Object storage&lt;/strong&gt; on S3, Azure Blob Storage and Cloud Storage, with parallel transfers, presigned URLs and
conditional writes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NoSQL documents&lt;/strong&gt; on DynamoDB, Cosmos DB and Firestore, with secondary indexes and transactions, plus Spring
Data repositories with derived queries, optimistic locking and keyset scrolling.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Messaging&lt;/strong&gt; on SNS and SQS, Service Bus and Pub/Sub: listeners, batched acknowledgements, sessions, claim
checks and tracing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Events&lt;/strong&gt; as CloudEvents 1.0, published to EventBridge, Event Grid namespace topics and Eventarc Advanced.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Streams&lt;/strong&gt; on Amazon MSK, Event Hubs and Managed Service for Apache Kafka, with consumer groups, retries, dead
letters, and idempotent and transactional producers, plus Spring for Apache Kafka with Halyx's cloud login.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SQL databases&lt;/strong&gt; through JDBC and R2DBC, logging in with RDS IAM, Entra ID or Cloud SQL IAM, or through the
Cloud SQL connector.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Caches&lt;/strong&gt; on ElastiCache, Azure Cache for Redis, Azure Managed Redis and Memorystore, with cloud IAM login and a
Spring &lt;code&gt;CacheManager&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parameters and secrets&lt;/strong&gt; from SSM Parameter Store, Secrets Manager, App Configuration, Key Vault, Parameter
Manager, Secret Manager and Vault, with Spring configuration import (&lt;code&gt;halyx:&lt;/code&gt;) and reload.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Credentials:&lt;/strong&gt; each cloud's default chain, plus assumed AWS roles, impersonated Google service accounts and
Entra ID service principals. That includes a federated token from another cloud, so no secret is stored.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://halyx.dev/docs/spring-boot.html&quot;&gt;Spring Boot&lt;/a&gt;:&lt;/strong&gt; properties for every service, health indicators, Micrometer metrics,
and &lt;code&gt;@HalyxListener&lt;/code&gt; methods.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://halyx.dev/docs/api/reactive.html&quot;&gt;Reactive and asynchronous APIs&lt;/a&gt;&lt;/strong&gt; for every service, non-blocking where the SDK's async
client allows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://halyx.dev/docs/portable-service.html&quot;&gt;A portable microservice sample&lt;/a&gt;&lt;/strong&gt; that runs one image unchanged on EKS, GKE and
AKS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://halyx.dev/docs/ai-skills.html&quot;&gt;AI skills&lt;/a&gt;&lt;/strong&gt; for migrating a Spring Boot service to Halyx, reviewing one for portability,
and creating a new one.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;modules&quot;&gt;Modules&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-0-0.html#modules&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;table-wrap&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Module&lt;/th&gt;
&lt;th&gt;What it adds&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;halyx-bom&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One version for every Halyx module, and for the Cloud SQL connector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;halyx-core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The portable API, provider SPI, error model, interceptors and an in-memory provider&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;halyx-aws&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;S3, DynamoDB, SNS, SQS, EventBridge, SSM Parameter Store, Secrets Manager, ElastiCache, RDS and MSK IAM login&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;halyx-azure&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Blob Storage, Cosmos DB, Service Bus, Event Grid namespaces, Key Vault, App Configuration, Event Hubs, and Entra ID login for caches and databases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;halyx-gcp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cloud Storage, Firestore, Pub/Sub, Eventarc Advanced, Secret Manager, Parameter Manager, Managed Kafka, and IAM login for Memorystore and Cloud SQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;halyx-vault&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HashiCorp Vault KV v2 parameters and secrets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;halyx-redis&lt;/code&gt;, &lt;code&gt;halyx-sql&lt;/code&gt;, &lt;code&gt;halyx-r2dbc&lt;/code&gt;, &lt;code&gt;halyx-kafka&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Redis and Valkey caches, a pooled JDBC &lt;code&gt;DataSource&lt;/code&gt;, a pooled R2DBC &lt;code&gt;ConnectionFactory&lt;/code&gt;, and Kafka streams, each with cloud IAM login&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;halyx-reactor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Mono&lt;/code&gt; and &lt;code&gt;Flux&lt;/code&gt; views of every service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;halyx-micrometer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Observations of every operation, and trace context carried through messages, events and stream records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;halyx-spring-data-document&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Spring Data repositories over DynamoDB, Cosmos DB and Firestore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;halyx-spring-boot-starter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Spring Boot auto-configuration, on Boot 3.5 and 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;halyx-tck&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The conformance kits, for testing a provider&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Each jar has a Java module name, such as &lt;code&gt;dev.halyx&lt;/code&gt; for &lt;code&gt;halyx-core&lt;/code&gt; and &lt;code&gt;dev.halyx.aws&lt;/code&gt; for &lt;code&gt;halyx-aws&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-0-0.html#testing&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every service passes the conformance kits on the in-memory provider, on each cloud's emulators and on the real
clouds, with Spring Boot 3.5 and 4. The latest results are in the
&lt;a href=&quot;https://halyx.dev/docs/cloud-testing/index.html&quot;&gt;cloud testing reports&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;known-limitations&quot;&gt;Known limitations&lt;a class=&quot;anchor&quot; href=&quot;https://halyx.dev/blog/halyx-1-0-0.html#known-limitations&quot; aria-label=&quot;Link to this section&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;div class=&quot;callout callout-note&quot; role=&quot;note&quot;&gt;&lt;p class=&quot;callout-title&quot;&gt;Note&lt;/p&gt;
&lt;p&gt;Not yet run on real clouds: MSK Serverless (the test account's plan doesn't allow it), Azure Database for MySQL
with Entra ID, and the service principal's certificate login.&lt;/p&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Some calls still block underneath, on a virtual thread: Cloud Storage, Service Bus receives, and Cosmos DB and
Firestore read-then-write calls.&lt;/li&gt;
&lt;li&gt;R2DBC to SQL Server is password-only, because its driver takes no access tokens.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href=&quot;https://halyx.dev/docs/roadmap.html&quot;&gt;roadmap&lt;/a&gt; has what comes next.&lt;/p&gt;
</content>
  </entry>
</feed>
