Repository navigation
Releases
Halyx 1.1.0
A minor release, compatible with 1.0. Upgrade by changing the halyx-bom version to 1.1.0; japicmp checks every published module against 1.0.0, so existing code keeps compiling and running. 1.1.x now gets the security fixes (see SECURITY.md).
Features
-
Kotlin coroutines (#11):
@HalyxListenermethods may besuspend funs, for subscriptions and stream consumers. Returning acks or
commits, and throwing retries, as with any listener. Needskotlinx-coroutines-reactor.- Reactive repositories take
suspendCRUD methods and derived queries, andFlowresults: aFlowquery reads
every match, page by page, and asuspendquery may return aWindow. Entities are@JvmRecord data classes. - Tested on Boot 3.5 (Kotlin 1.9) and Boot 4 (Kotlin 2.3), in memory and on Kafka, SQS and DynamoDB emulators,
withawaitSingleandasFlowover the reactive services.
-
Function invocation (#22):
FunctionInvokerinvokes AWS Lambda functions, Azure Functions and Cloud Run functions by name, with a payload,
configured and per-request headers, and a timeout, throughCloudContext.functionInvoker. Each cloud's login is
handled: IAM on Lambda, an Entra ID token or a function key on Azure, a Google ID token on Cloud Run.- A function that throws raises
UnavailableExceptionon every cloud, a missing oneNotFoundException, and
other statuses map as for every other service. Requests over a cloud's payload or header limit are refused
before they're sent. - Invocations run the function, so they're retried only for functions configured
idempotent; the SDKs retry
others only after throttling or a failure to connect. triggerstarts a Lambda function without waiting for it (FunctionCapability.TRIGGER).async()views, and
ReactiveFunctionInvokerin halyx-reactor.TracingFunctionInvokercarries the trace context into the function, as headers or in Lambda'sClientContext.- Spring Boot:
halyx.functions.<key>registers aFunctionInvokerbean, with a reactive view. - A function conformance kit, run on the memory provider (
InMemoryFunctions), Floci's Lambda, the Azure
Functions host and Google's Functions Framework, with tier 1 real-cloud runs on all three clouds. AzureErrors.fromStatusandGcpErrors.fromStatusmap an HTTP status as the SDK translations do.- The AI skills know functions:
halyx-ai-createplans, writes and tests function calls,halyx-ai-migratemaps
LambdaClientand HTTP calls to Azure Functions and Cloud Run functions, andhalyx-ai-reviewchecks them
(rule C11).
-
Rotated keys (#26):
FunctionConfig.Builder.property(String, Supplier<String>): a property asked for each time it's used. The
Azure function key is asked for on every call, so a rotated key takes effect without a restart.- Spring Boot:
halyx.functions.<key>.secretsreads settings such asazure.functions.keyfrom one of the
halyx.parameter-stores, through its read cache. A missing secret fails the start. - Spring Boot: a
halyx.*property whose${...}placeholder didn't resolve fails the start, naming the property.
Spring leaves such a value as written, so a missing secret was sent as the key and failed every call. - Cosmos DB account keys and Event Grid access keys given as suppliers (
ProviderConfig.property(String, Supplier),
and the same onEventBusConfigandSubscriptionConfig) are asked for everyazure.key-refresh-interval(one
minute by default), and Cosmos DB and Event Grid use a rotated key from the next request. Spring Boot:
secretson providers, event buses and subscriptions. - Service Bus and App Configuration connection strings stay read once: their SDKs copy the key when the client is
built. The docs recommend Entra ID login for keys that rotate. - Cache and Kafka passwords given as suppliers (
CacheAuth.password(Supplier),
StreamAuth.password(mechanism, username, Supplier)) are asked for each time a connection logs in: Redis on
new connections and reconnects, Kafka also when the broker has open connections log in again. Spring Boot:
auth.secreton caches, the cache manager and stream clusters, as on databases, including for Spring for
Apache Kafka. - Docs: keeping keys for other services in a parameter store, including a store on another cloud.
- Spring Boot: a blank
propertiesvalue no longer counts as set next to asecretsentry for the same setting,
as a blank value is unset everywhere else. - Real-cloud tests on Azure (tier 1): a function key, a Cosmos DB key and an Event Grid key rotated in Key Vault
while the clients stay open, and the function key through Spring Bootsecrets.
-
Function error text (#31): a failed invocation's exception
has aFunctionErrorcause with what the function reported, whole: Lambda'serrorMessage,errorTypeand
payload, or an HTTP function's status and body.FunctionError.of(e)finds it. The exception messages are
unchanged and still cut long bodies. On the memory provider, a handler's exception is now theFunctionError's
cause rather than theUnavailableException's.
Build
- API compatibility checks (#4):
mvn verifycompares each
published module with 1.0.0 from Maven Central using japicmp, and fails on a binary- or source-incompatible change
to the public API, the provider SPI included. CI runs it on every push. - Dependency scanning: a workflow scans SBOMs of every published module and the samples with OSV-Scanner on
every push and pull request, and weekly.osv-scanner.tomllists the findings Halyx can't fix itself, with their
reasons, in place of the Snyk policy file.
Dependencies
- OpenTelemetry API 1.67.0, held above the 1.62.0 the Google Cloud libraries bring, for a security fix found
with OSV-Scanner. The portable orders samples take the same override, and the Boot 3.5 one also log4j 2.25.5. - Tested on Spring Boot 4.1.2 and Google Cloud libraries 26.91.0 (Parameter Manager 0.44.0).
- Spring Boot 3.5 applications: Boot 3.5.16, the newest 3.5 release, manages Spring Framework 6.2.19, for which
OSV lists advisories fixed only in Spring Framework 7. Halyx doesn't bring Spring Framework; the version is the
application's Boot's.
Halyx 1.0.2
A patch release. Upgrade by changing the halyx-bom version to 1.0.2; there are no API changes.
Fixes
- JSON cache codec on Spring Boot 4 (#9): with
halyx.cache-manager.codec=jsonand Jackson 2 on the classpath too, ashalyx-azurebrings it, the codec used
Jackson 2 without Java date/time support, so caching a value with anInstantfailed. Jackson 3 is now always
preferred when present. - Cancelling a reactive call (#12): cancelling a
ReactiveHalyx
call that had started, bytimeout()for example, interrupted its thread on the default virtual-thread scheduler
and onboundedElastic, which could abort a blocking SDK call partway through. A started call now runs to
completion and its result is dropped, as documented; one cancelled before it starts never runs. The docs now also
say that calls on the non-blocking path are cancelled with the subscription, and the driver may abort the request.
Samples
- Plain Java quickstart (#2):
halyx-samples/plain-java-quickstart
object storage, messaging and parameters without Spring, on the in-memory provider by default or on a cloud through
system properties; linked from the README and Getting started. Contributed in
#8.
Thanks @omisha123 for the plain Java quickstart (#8)!
Halyx 1.0.1
A patch release. Upgrade by changing the halyx-bom version to 1.0.1; there are no API changes.
Fixes
- Gradle with Spring Boot 3.5:
halyx-aws,halyx-azureandhalyx-gcpfailed to resolve, becausehalyx-kafka
brought both the patchedat.yawk.lz4:lz4-javaand theorg.lz4:lz4-javathat Boot 3.5'skafka-clientsdepends
on, and Gradle refuses two modules with the same capability.halyx-kafkanow excludesorg.lz4:lz4-java, so
only the patched one is on the classpath, with Maven too. ReactiveBlobStore.getBodycloses the blob before the completion or error reaches the subscriber, as
documented, rather than just after it.
Documentation
- Gradle (Kotlin DSL and Groovy) dependency snippets in Getting started and the Spring Boot guide (#7). Thanks @yoreva-01, the first outside contribution to Halyx!
Halyx 1.0.0
The first release: one Java API for object storage, NoSQL documents, messaging, events, streams, SQL databases,
caches, and parameters and secrets on AWS, Azure and Google Cloud, over each cloud's own SDKs.
Requirements
- Java 21 or later.
- For Spring Boot applications, Spring Boot 3.5 or 4.
Modules
| Module | What it adds |
|---|---|
halyx-bom |
One version for every Halyx module, and for the Cloud SQL connector |
halyx-core |
The portable API, provider SPI, error model, interceptors and an in-memory provider |
halyx-aws |
S3, DynamoDB, SNS, SQS, EventBridge, SSM Parameter Store, Secrets Manager, ElastiCache, RDS and MSK IAM login |
halyx-azure |
Blob Storage, Cosmos DB, Service Bus, Event Grid namespaces, Key Vault, App Configuration, Event Hubs, and Entra ID login for caches and databases |
halyx-gcp |
Cloud Storage, Firestore, Pub/Sub, Eventarc Advanced, Secret Manager, Parameter Manager, Managed Kafka, and IAM login for Memorystore and Cloud SQL |
halyx-vault |
HashiCorp Vault KV v2 parameters and secrets |
halyx-redis, halyx-sql, halyx-r2dbc, halyx-kafka |
Redis and Valkey caches, a pooled JDBC DataSource, a pooled R2DBC ConnectionFactory, and Kafka streams, each with cloud IAM login |
halyx-reactor |
Mono and Flux views of every service |
halyx-micrometer |
Observations of every operation, and trace context carried through messages, events and stream records |
halyx-spring-data-document |
Spring Data repositories over DynamoDB, Cosmos DB and Firestore |
halyx-spring-boot-starter |
Spring Boot auto-configuration, on Boot 3.5 and 4 |
halyx-tck |
The conformance kits, for testing a provider |
Each jar has a Java module name, such as dev.halyx for halyx-core and dev.halyx.aws for halyx-aws.
Highlights
- Object storage on S3, Azure Blob Storage and Cloud Storage, with parallel transfers, presigned URLs and
conditional writes. - NoSQL documents on DynamoDB, Cosmos DB and Firestore, with secondary indexes and transactions, and Spring Data
repositories with derived queries, optimistic locking and keyset scrolling. - Messaging on SNS and SQS, Service Bus and Pub/Sub: listeners, batched acknowledgements, sessions, claim checks
and tracing. - Events as CloudEvents 1.0, published to EventBridge, Event Grid namespace topics and Eventarc Advanced.
- Streams on Amazon MSK, Event Hubs and Managed Service for Apache Kafka, with consumer groups, retries, dead
letters, and idempotent and transactional producers; Spring for Apache Kafka with Halyx's cloud login. - SQL databases through JDBC and R2DBC, logging in with RDS IAM, Entra ID or Cloud SQL IAM, or through the Cloud
SQL connector. - Caches on ElastiCache, Azure Cache for Redis, Azure Managed Redis and Memorystore, with cloud IAM login, and a Spring
CacheManager. - Parameters and secrets from SSM Parameter Store, Secrets Manager, App Configuration, Key Vault, Parameter
Manager, Secret Manager and Vault, with Spring configuration import (halyx:) and reload. - Credentials: each cloud's default chain, plus assumed AWS roles, impersonated Google service accounts and
Entra ID service principals, including a federated token from another cloud, with no stored secret. - Spring Boot: properties for every service, health indicators, Micrometer metrics, and
@HalyxListenermethods. - Reactive and asynchronous APIs for every service, non-blocking where the SDK's async client allows.
- A portable microservice sample that runs one image unchanged on EKS, GKE and AKS.
- AI skills for migrating a Spring Boot service to Halyx, reviewing one for portability, and creating a new one.
Testing
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
cloud testing reports.
Known limitations
- 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. - Some calls still block underneath, on a virtual thread: Cloud Storage, Service Bus receives, and Cosmos DB and
Firestore read-then-write calls. - R2DBC to SQL Server is password-only, because its driver takes no access tokens.
See the roadmap for what comes next.
Getting it
Import the BOM from Maven Central:
<dependency>
<groupId>dev.halyx</groupId>
<artifactId>halyx-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>See Getting started.