LittleHorse Runtime (Quarkus)
This guide will walk you through using the LittleHorse Runtime Extension for Quarkus in a complete local development setup.
You will do the following:
- Spin up a local LittleHorse environment using Docker
- Spin up a local Quarkus environment that depends on the LittleHorse Runtime Extension for Quarkus
- LittleHorse Runtime will register our
WfSpec
andTaskDef
s, and start Task Workers according to our code
- LittleHorse Runtime will register our
- Query Quarkus REST endpoints that wrap LittleHorse gRPC client functionality, such as:
- Running a
WfRun
- Getting the state of
WfRun
Variables - Posting an
ExternalEvent
- Searching for
WfRun
s using variable filters
- Running a
What is LittleHorse Runtime?
LittleHorse Runtime is a powerful Quarkus-based runtime for interacting with LittleHorse Kernel and Pony ID. Packaged as a Quarkus Extension, LittleHorse Runtime can perform automatic Workflow, Task, and UserTask Registration. The runtime can also manage the lifecycle of your Task Workers and report Health Checks using SmallRye Health. Out of the box, we provide several beans for injecting a LittleHorse gRPC client into your services. When you combine a LittleHorse gRPC client bean with other Quarkus extensions, such as Quarkus REST, the runtime becomes a powerful tool for integrating LittleHorse Client functionality into external systems.
When you're ready to put LittleHorse Runtime into production, Quarkus allows you to turn your JVM application into a native executable optimized to run in serverless and containerized environments. LittleHorse Enterprises has an upcoming product will allow you to run applications using the LH Runtime in a serverless application, stay tuned for that 😉.
Setup
Your system needs:
- Java 17 or greater
- Docker configured with at least 4GB of RAM
First, we will run a LittleHorse Server and Dashboard for development purposes in docker (using the lh-standalone
docker image):
Make sure you have at least 2GB of RAM allocated to Docker and ports 2023, 8080, and 9092 must be free.
docker run --pull always --name lh-standalone --rm -d -p 2023:2023 -p 8080:8080 -p 9092:9092 \
ghcr.io/littlehorse-enterprises/littlehorse/lh-standalone:latest
If you are on Linux, you can simplify the command to run the container by making use of Docker's host
network. This option has multiple bugs with MacOS:
docker run --pull always --name lh-standalone --rm -d --net=host \
ghcr.io/littlehorse-enterprises/littlehorse/lh-standalone:latest
Other Dependencies
You'll need to clone our lh-examples
repository on GitHub:
git clone https://github.com/littlehorse-enterprises/lh-examples
# Move into the LH Runtime Quickstart directory
cd lh-examples/quickstart/lh-quarkus
Running the Quickstart
The quickstart workflow we will use today models an Identity Verification flow.
Starting LH Runtime
To start your LH Runtime application for development, just run the following command:
./gradlew quarkusDev
This application will expose a few REST endpoints at localhost:9000
. We'll use these endpoints to interact with and query workflow data from LittleHorse.
The LH Runtime extension for Quarkus will automatically register any WfSpec
s, TaskDef
s, and UserTask
s defined in your codebase that use the provided LH Runtime annotations, such as @LHWorkflow
and @LHTask
. These signal to LH Runtime that it should manage the lifecycle and execution of your workflows and tasks.
Take a look at the following classes:
IdentityVerificationWorkflow.java
defines theWfSpec
logic.VerifyIdentityTask.java
defines ourverify-identity
TaskDef
.
As you can see, with LH Runtime you just need to define your WfSpec
s and TaskDef
s in code, while LH Runtime will take care of the registration of those objects. The LH Runtime extension will also start Task Workers for the TaskDef
s define in your code.
You can check out the workflow in the LittleHorse Dashboard:

Run the WfRun
Let's run the workflow! We're going to verify the identity of Obi-Wan Kenobi.
In the IdentityResource
class, we defined a POST endpoint /identity-verification/start
that takes in a JSON Body with a full name, email, and SSN. This endpoint calls the IdentityService
to handle the business logic of starting our Identity Verification workflow. This endpoint returns the ID of your Identity Verification WfRun
so you can query the status later.
Using your HTTP client of choice, make a POST
call to the /identity-verification/start
endpoint with the following JSON body:
- Raw JSON
- cURL
{
"fullName": "Obi-Wan Kenobi",
"email": "obiwankenobi@jedi.temple",
"ssn": 123456789
}
curl -X POST http://localhost:9000/identity-verification/start \
-H "Content-Type: application/json" \
-d '{
"fullName": "Obi-Wan Kenobi",
"email": "obiwankenobi@jedi.temple",
"ssn": 123456789
}'
This endpoint should return a JSON Object with a unique WfRunId
, for example:
{
"wfRunId": "374063862026437a9d9e289280ea4bf1"
}
Save this WfRunId
for later, we'll use it to query the status of the Identity Verification workflow.
Dashboard View
In the LittleHorse Dashboard, you'll see the WfRun
waiting for an ExternalEvent
as follows:

Notice the variables at the bottom of the dashboard, in the next step we will query an endpoint that reveals the status of our Identity Verification workflow based on these variables.

Get the State of WfRun
Variables
Next, let's get the status of our Identity Verification WfRun
, which is stored in a few WfRun
variables.
In the IdentityResource
class, we defined a GET endpoint at /identity-verification/status/{wfRunId}
. This endpoint calls the VariablesService
to handle the business logic of searching for variables in our WfRun
, specifically the full-name
, email
, and approval-status
variables.
Using your HTTP client of choice, make a GET
call to the /identity-verification/status/{wfRunId}
endpoint, replacing {wfRunId}
with the ID we received in the last step:
- cURL
curl -X GET http://localhost:9000/identity-verification/status/{wfRunId}
At this stage in the workflow, our API should respond that the Identity Verification is PENDING:
{
"fullName": "Obi-Wan Kenobi",
"email": "obiwankenobi@jedi.temple",
"status": "PENDING",
"wfRunId": "374063862026437a9d9e289280ea4bf1"
}
Posting an ExternalEvent
Peeking back at the dashboard, you will notice that the verify-identity
task completed successfully but, the workflow is still in the RUNNING
state and our approval-status
is still PENDING
. This is because the workflow is waiting for an external event identity-verified
to be posted to the workflow.
Normally in a real-world application, you would have some other service that would post an event to the workflow with webhooks. For this example, we will use another endpoint to post this event to the workflow, simulating a webhook.
The WfSpec
utilizes the Correlated Events feature such that the identity-verified
event can be matched to a waiting WfRun
by the email address. This means our external system doesn't need to know the WfRunId
of any pending workflows for this identity!
Using your HTTP client of choice, make a POST
call to the /identity-verification/verify
endpoint with the following JSON Body:
- Raw JSON
- cURL
{
"email": "obiwankenobi@jedi.temple",
"isValid": true
}
curl -X POST http://localhost:9000/identity-verification/verify \
-H "Content-Type: application/json" \
-d '{
"email": "obiwankenobi@jedi.temple",
"isValid": true
}'
Now if we fetch our Identity Verification status again, you will see that the workflow has completed and Obi-Wan has been notified that their identity has been verified.
{
"fullName": "Obi-Wan Kenobi",
"email": "obiwankenobi@jedi.temple",
"status": "APPROVED",
"wfRunId": "374063862026437a9d9e289280ea4bf1"
}
You can see these changes reflected on the LittleHorse Dashboard:

Search for WfRun
s using variable filters
Lastly, let's look at a more advanced use case that combines multiple LittleHorse gRPC calls into one REST endpoint.
In the IdentityResource
class, we defined a GET endpoint /identity-verification/status/search
that takes in 3 query parameters: two search filters email
and status
, and a bookmark
for pagination. This endpoint returns a list of Identity Verification statuses matching your search criteria.
This endpoint calls two services and two LittleHorse gRPC endpoints under the hood:
- Calls the
WorkflowsService
to perform aRPC SearchWfRun
request. This searches for manyWfRun
s filtered byemail
andstatus
, and returns a list ofWfRunId
s matching our search. - Calls the
IdentityService
to perform multipleRPC GetVariable
requests. This fetches the status of each Identity Verification workflow, based on the list ofWfRunId
s from step 1.
Using your HTTP client of choice, make a GET
call to the /identity-verification/status/search
endpoint with the status=APPROVED
and or email=obiwankenobi@jedi.temple
query parameters:
- cURL
curl -X GET "http://localhost:9000/identity-verification/status/search?status=APPROVED"
At this stage in the workflow, our API should respond with one search result:
{
"results": [
{
"fullName": "Obi-Wan Kenobi",
"email": "obiwankenobi@jedi.temple",
"status": "APPROVED",
"wfRunId": "374063862026437a9d9e289280ea4bf1"
}
],
"bookmark": ""
}
Now, we've combined many LittleHorse gRPC requests into one REST endpoint.
Wrapping Up
Congratulations on completing the LH Runtime quickstart!
With a few handy annotations, LH Runtime can automatically deploy our WfSpec
, TaskDef
s, and start our Task Worker
s. It also provides us access to the LittleHorse gRPC client bean, which we can combine with other Quarkus extensions like Quarkus REST to abstract away the gRPC endpoints and define our own API for users to interact with.
If you've made it this far, join us on Slack and give us a star on GitHub!
For further reading, check out the LH Runtime Extension for Quarkus on GitHub.