Redouble AI Nucleo documentation
Part 9 · Hosting Nucleo

Running inside Quarkus

ai.redouble.nucleo.quarkus · nucleo-quarkus

To run Nucleo inside a Quarkus application, add one dependency, ai.redouble:nucleo-quarkus. It is the Quarkus counterpart of the Spring Boot starter, and it wires the same things:

It needs quarkus-arc and MicroProfile Config and no web framework, so it serves a REST application and a batch process the same way. It is a Quarkus extension: this runtime module and a deployment module, nucleo-quarkus-deployment, whose build steps make the runtime work in a native image.

Credentials

A provider asks the runtime for a credential by the id it declares, anthropic-api-key or aws-region, and the extension's store, QuarkusSecrets, answers from the property of that name under nucleo.credentials. The application binds the property the way it binds quarkus.datasource.password:

nucleo.credentials.anthropic-api-key=${ANTHROPIC_API_KEY:}
nucleo.credentials.aws-region=${AWS_REGION:}
nucleo.credentials.aws-access-key-id.user=${AWS_ACCESS_KEY_ID:}
nucleo.credentials.aws-access-key-id=${AWS_SECRET_ACCESS_KEY:}

The property itself is the credential's secret; .user and .host are its other two parts. A system property, a profile, or a config source of the deployment's own all bind it.

A part no property binds is read from the environment variable of the runtime's one rule (ANTHROPIC_API_KEY, AWS_REGION, AZURE_FOUNDRY_API_KEY with AZURE_FOUNDRY_API_KEY_HOST for the endpoint), part by part, so an application that binds nothing runs with the variables exported, and a bound part always wins. A "not configured" refusal names both the property and the variable. Every id and its parts are listed in Credentials.

Settings

Every runtime setting is a field on a settings class next to the code it governs, and in a Quarkus application it binds from a nucleo.* property (Settings and the configurator lists them all). The properties are bound when the container starts, before the dispatcher runs, and a property wins over whatever a configurator set:

nucleo.http.pool-size=2000
nucleo.model.mantle-lax-project=proj_...

The spelling of every property is the same as in a Spring Boot application, so a setting reads the same in either host. An application that names its own configurator with -Dnucleo.env replaces the extension's QuarkusEnvironment, which is the one that points the credential store at the properties above; its configurator then assigns QuarkusSecrets to SecretsSettings.secretsClass itself when it wants credentials from the properties.

Using the runtime from your beans

Submit work through JobDispatcher.getInstance(), or inject the extension's NucleoRuntime bean: its dispatcher() is the same dispatcher, and its ledger() is the CostLedger holding what every workflow has spent so far. The dispatcher starts before any StartupEvent observer of the application's own at default priority, so such an observer may submit a first job.

In dev mode a live reload, and in tests a switch of test profile, restarts the application inside the same JVM. The dispatcher keeps running across those restarts, and is shut down only when a normal run of the application ends.

The application's database

When jobs call the application's own Panache repositories, an injected EntityManager, a @Transactional bean or an Agroal DataSource, the extension can count the jobs using the database at once, so they never outnumber the pool:

nucleo.database.max-concurrent=20
nucleo.database.name=default

With the bound set, the extension registers the core CountingDBResourceProvider when the container starts, and a job declares it before it touches the database, exactly as in a Spring Boot application (Spring Boot, Admission). Without max-concurrent nothing is registered.

Native image

Because nucleo-quarkus is a Quarkus extension, a native build of the application produces a working native image with no native configuration from the application: the extension registers what the runtime reaches by reflection, the resources it reads, and the parts that must start at run time.

The one rule the extension keeps is that no third-party dependency's native metadata is written by hand: a dependency reaches the native image through its own Quarkus extension, or stays off the native build. So the direct Anthropic provider, whose Kotlin SDK has no native support, runs on the JVM only, as does Claude on Bedrock through the Anthropic SDK; a native image reaches Claude through nucleo-provider-bedrock, on the AWS SDK. The AWS SDK's native support comes from its own Quarkus extension, io.quarkiverse.amazonservices:quarkus-amazon-bedrockruntime, which an application calling Bedrock from a native image adds to its own dependencies; without it the native build fails on the SDK's optional CRT paths. The Quarkus host of the demo is built both ways.

How it works inside

The extension's classes, the deployment module's build steps and the native substitutions are in Inside the Quarkus extension, for those working on the runtime itself.

^