Pony ID
The open-source LittleHorse Kernel natively supports User Tasks. However, the LittleHorse Kernel does not support the concept of user identity as it would require adding significant amounts of complexity and external dependencies to the open-source project.
The User Tasks Bridge solves this problem by allowing you to connect your own Identity Provider (IdP) to LittleHorse.
While the LittleHorse Kernel is open-source, the User Tasks Bridge is a paid product of LittleHorse Enterprises. The docker image present in this quickstart is suitable for local development but you should contact sales@littlehorse.io
for production deployment of the User Tasks Bridge.
You can use open-source User Tasks in production without the User Tasks Bridge. To do so, we recommend checking out the open-source User Tasks Documentation to understand how to use User Tasks in your workflows.
Quickstart
This guide will walk you through using the User Tasks Bridge in a complete local development setup.
You will do the following:
-
Spin up a local LittleHorse + User Tasks Bridge environment using Docker
-
Define a
userTaskDef
for approving an IT rental -
Register supporting
taskDef
s for handling the workflow -
Create a
wfSpec
that includes a human approval step -
Use the User Tasks Bridge Console to view and complete assigned tasks
Step 1: Running Locally with Docker
Start the standalone container:
Linux:
docker run --pull always --name lh-user-tasks-bridge-standalone --rm -d --net=host \
ghcr.io/littlehorse-enterprises/lh-user-tasks-bridge-backend/lh-user-tasks-bridge-standalone:latest
Windows/Mac:
docker run --pull always --name lh-user-tasks-bridge-standalone --rm -d \
-p 8080:8080 \
-p 8888:8888 \
-p 8089:8089 \
-p 3000:3000 \
-p 2023:2023 \
ghcr.io/littlehorse-enterprises/lh-user-tasks-bridge-backend/lh-user-tasks-bridge-standalone:latest
The standalone container starts the following components:
- LittleHorse Kernel (gRPC 2023)
- LittleHorse Dashboard (http://localhost:8080)
- Keycloak Identity Provider (http://localhost:8888)
- User Tasks Bridge Backend (http://localhost:8089)
- User Tasks Bridge Console (http://localhost:3000)
Step 2: Defining a Workflow with the LH Kernel
Creating the userTaskDef
Before we can define our wfSpec
, we first need to register the it-approval userTaskDef
that will be used within our workflow.
To register a userTaskDef
, we will:
- Define the fields that will be used in the
userTask
form - Construct the
userTaskDefRequest
object - Register the
userTaskDef
with the LH Kernel
- Java
- Python
- Go
- C#
import io.littlehorse.sdk.common.config.LHConfig;
import io.littlehorse.sdk.common.proto.PutUserTaskDefRequest;
import io.littlehorse.sdk.usertask.UserTaskSchema;
import io.littlehorse.sdk.usertask.annotations.UserTaskField;
class ApprovalForm{
// Define the fields that will be used in the user task form
@UserTaskField(displayName = "approve it rental" , required = true)
public boolean isApproved;
}
public class ApprovalTask{
public static void main(String[] args) {
LHConfig config = new LHConfig();
// Create the user task schema and compile it into a `PutUserTaskDefRequest`
UserTaskSchema schema = new UserTaskSchema(new ApprovalForm(), "approve-it-rental");
PutUserTaskDefRequest userTask = schema.compile();
// Register the `userTaskDef` with the LH Kernel
config.getBlockingStub().putUserTaskDef(userTask);
}
}
//TODO
//TODO
//TODO
Creating the TaskDef
s
Now that the userTaskDef
is registered with the LH Kernel, we need to create, register, and start our taskWorkers
.
We will create two different taskDef
s:
One task for shipping the IT item when the rental is approved
One task to notify the employee that the rental was not approved
- Java
- Python
- Go
- C#
import io.littlehorse.sdk.common.config.LHConfig;
import io.littlehorse.sdk.worker.LHTaskMethod;
import io.littlehorse.sdk.worker.LHTaskWorker;
class TaskWorker {
@LHTaskMethod("ship-item")
public void shipItem(String item){
System.out.println(item + " shipped");
}
@LHTaskMethod("decline-order")
public void cancelOrder(String employee, String item ){
System.out.println("Sorry "+ employee + " you have not been approved for " + item);
}
}
public class RegisterWorkers{
public static void main(String[] args){
LHConfig config = new LHConfig();
//Create and register TaskDefs
LHTaskWorker shipItemTask = new LHTaskWorker(new TaskWorker(), "ship-item" ,config );
shipItemTask.registerTaskDef();
LHTaskWorker notApprovedTask = new LHTaskWorker(new TaskWorker(), "decline-order", config);
notApprovedTask.registerTaskDef();
// Start the TaskWorkers
shipItemTask.start();
notApprovedTask.start();
}
}
//TODO
//TODO
//TODO
Creating the wfSpec
With both the userTaskDef
and taskDef
registered, it is time to create the wfSpec
The wfSpec
below does the following:
- Declares the variables that will be used in the workflow
- Assigns the
userTaskDef
to thesomeemailaddress@somedomain.com
, the email ofmy-user
- Uses the
doIf
to handle the approval logic- Executes the
ship-item
task if the rental is approved, or thedecline-order
task if the rental is not approved
- Java
- Python
- Go
- C#
import io.littlehorse.sdk.common.config.LHConfig;
import io.littlehorse.sdk.wfsdk.NodeOutput;
import io.littlehorse.sdk.wfsdk.WfRunVariable;
import io.littlehorse.sdk.wfsdk.Workflow;
import io.littlehorse.sdk.wfsdk.WorkflowThread;
public class UTBWorkflow {
public static void wfLogic(WorkflowThread wfThread){
WfRunVariable item = wfThread.declareStr("item").required();
WfRunVariable employee = wfThread.declareStr("employee").required();
WfRunVariable isApproved = wfThread.declareBool("is-approved");
NodeOutput result = wfThread.assignUserTask("approve-it-rental", "someemailaddress@somedomain.com", users);
isApproved.assign(result.jsonPath("$.isApproved"));
wfThread.doIf(isApproved.isEqualTo(false), ifHandler ->{
ifHandler.execute("decline-order", item, employee);
ifHandler.complete();
});
wfThread.execute("ship-item", item);
}
public static void main(String[] args){
LHConfig config = new LHConfig();
Workflow wfGenerator = Workflow.newWorkflow("it-request", UTBWorkflow::wfLogic);
wfGenerator.registerWfSpec(config.getBlockingStub());
}
}
//TODO
//TODO
//TODO
Step 3: Run the Workflow
Now that our wfSpec
is registered, we can run the workflow using the LittleHorse CLI.
Run the following lhctl
command to start the workflow:
lhctl run it-request item laptop employee obi-wan
Step 4: Logging into the User Tasks Bridge Console and Completing the Task
User Task Bridge Console
Now that the workflow is running, we can log into the User Task Bridge Console to view and complete the UserTaskRun
Credentials:
User | Pass | Realm |
---|---|---|
my-user | 1234 | default |
my-admin-user | 1234 | default |
admin | admin | master |
User Tasks Bridge Console (http://localhost:3000)
- Sign in as my-admin-user to view and manage all
userTask
s.
When you log in as my-admin-user
, you will see the userTaskRun
that was created by the workflow.
Do not complete the task yet! We will complete the task as my-user
in the next step.

if you navigate to the users
tab, you will notice that the my-admin-user is able to manage all users and groups in the default
realm.

If you navigate back to userTaskDefs
, you will see the userTaskDef
that was registered for the approve-it-rental
task and assigned to the user my-user
.
If you click on the approve-it-rental
userTask
, as the admin, you will be able to view the details of the userTask
, as well as complete the userTask
or assign it to another user.

Keycloak Identity Provider (http://localhost:8888)
- Sign in as admin with realm master to view and manage all users and groups. As well as create new users,
userGroups
, and realms.

Viewing and Completing userTasks
User Tasks Bridge Console (http://localhost:3000)
Log back into the User Tasks Bridge Console as my-user
.
Once logged in, you will see the userTaskRun
that was created by the workflow. You can click on the task to view the details and complete the task.
The User Task Bridge Console also allows filtering of userTask
s by userTaskRun
status, userGroups
, and date range.

You can complete the task by filling out the form and clicking the "Complete Task" button. Once you complete the task, the workflow will continue.

To view the completed workflow, you can navigate to the LittleHorse Dashboard (http://localhost:8080) and view the workflow run.

To view the userTaskRun
details with lchtl
you can run the following command:
lhctl get userTaskRun <wfRunId> <userTaskGuid>
{
"id": {
"wfRunId": {
"id": "b82c06f0c34f4966941d8c016ac9b88a"
},
"userTaskGuid": "2230d990106f475c9d4c6ac8dc471614"
},
"userTaskDefId": {
"name": "approve-it-rental",
"version": 0
},
"userId": "someemailaddress@somedomain.com",
"results": {
"isApproved": {
"bool": true
}
},
"status": "DONE",
"events": [
{
"time": "2025-06-23T01:06:52.921Z",
"assigned": {
"newUserId": "someemailaddress@somedomain.com"
}
}
],
"scheduledTime": "2025-06-23T01:06:52.941Z",
"nodeRunId": {
"wfRunId": {
"id": "b82c06f0c34f4966941d8c016ac9b88a"
},
"threadRunNumber": 0,
"position": 1
},
"epoch": 1
}
Ready to Use Your Own IdP?
If you're interested in integrating the User Tasks Bridge with your organization's Identity Provider, we're here to help! Contact us at sales@littlehorse.io to learn more about our offerings and how we can support your production deployment needs.
This quickstart is intended for demonstration and testing purposes only. It uses a pre-configured Keycloak instance with default credentials and is not suitable for production environments.
The container automatically registers and runs a demo workflow called user-tasks-bridge-demo
to demonstrate the capabilities of using an Identity Provider (IdP) to manage UserTasks in LittleHorse. You'll be able to interact with these demo tasks through the User Tasks UI immediately after logging in.
Keep in mind that you can still register and run your own workflows and not limited to the demo workflow.