Skip to content
Latest (currently 2026.8.0) has the newest features, bug fixes, and CVE patches of Solo Enterprise for agentregistry.

For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.

Keycloak

Page as Markdown

Deploy Keycloak as the OIDC identity provider for agentregistry and configure the required clients and realm settings.

Solo Enterprise for agentregistry is protected by an identity provider (IdP) so that only authorized users can access, publish, and deploy AI artifacts. This guide walks through deploying Keycloak as the IdP, creating the required OIDC clients, and configuring the registry server and CLI to use them.

Before you begin

  1. Install the following CLIs.

    • helm: Install the Solo Enterprise for agentregistry Helm chart.
    • kubectl: Interact with your Kubernetes cluster.
    • aws: Interact with your agent in AWS Bedrock AgentCore.
    • jq: Used as a script wrapper in this guide.
    • openssl: Generate a unique private key for JWT signing. Install with brew install openssl.
  2. Set up and start a Docker Engine on your local machine, such as Docker Desktop so that you can work with Kind clusters. Solo Enterprise for agentregistry also uses Docker Compose to build Docker images and spin up AI artifacts.

  3. Create or use an existing Kubernetes cluster. For local test setups, you can create a Kind cluster.

    kind create cluster

Step 1: Configure Keycloak

Solo Enterprise for agentregistry does not bundle its own identity provider. Instead, it relies on an external OIDC provider to authenticate users, issue access tokens, and enforce role-based access control.

Before you deploy Keycloak, configure it by creating a ConfigMap that defines the realm, clients, protocol mappers, groups, and users that Solo Enterprise for agentregistry requires. Keycloak reads this ConfigMap at startup and automatically creates these resources, so you do not have to configure them manually through the Keycloak UI.

The OIDC client configuration differs depending on the runtime you want to connect Solo Enterprise for agentregistry to.

The following ConfigMap defines the resources that are required to connect Solo Enterprise for agentregistry to AWS Bedrock AgentCore, Azure AI Foundry, Microsoft Copilot Studio, and a virtual runtime that is powered by Solo Enterprise for agentgateway.

ResourceNameDescription
RealmagentregistryA dedicated Keycloak realm that isolates agentregistry’s identity configuration from other applications. The registry server uses the realm’s issuer URL to discover OIDC endpoints.
Clientar-backendA confidential client used by the registry server to validate access tokens issued by the CLI and UI clients.
Clientar-cli-interactiveA public client for the arctl CLI. Uses the device authorization grant (RFC 8628), which lets users log in from a terminal by opening a printed URL in a browser.
Clientar-cli-passwordA public client for scripted user authentication in CI/CD pipelines. Uses the password credentials grant with a username and password.
Clientar-uiA public client for the browser-based registry UI. Uses the authorization code flow with PKCE. The redirectUris and webOrigins are set to * for local testing. In production, replace these with the actual origin your UI uses.
Clientar-mcp-clientA public client for MCP clients such as Claude Code and Codex. Uses the authorization code flow with PKCE. Redirect URIs are restricted to http://localhost:* so that local MCP clients can receive the OAuth callback without using Dynamic Client Registration.
Protocol mappergroupsAn oidc-group-membership-mapper that writes the user’s Keycloak group memberships into the Groups JWT claim. The registry reads this claim to determine RBAC roles. Added to access tokens issued for ar-cli-interactive, ar-cli-password, ar-ui, and ar-mcp-client clients.
Protocol mapperar-backend-audienceAn oidc-audience-mapper that adds ar-backend to the aud claim of access tokens. Without the correct aud claim, the registry server rejects tokens with a 401 Unauthorized error. Added to access tokens issued for ar-cli-interactive, ar-cli-password, ar-ui, and ar-mcp-client clients.
GroupadminsA group that grants full admin access in the registry. Assigned to admin-user so they have superuser rights on first login.
Useradmin-userA test admin user with the password password, assigned to the admins group.
  1. Create the Keycloak namespace.

    kubectl create namespace keycloak
  2. Create the ConfigMap with your Solo Enterprise for agentregistry OIDC configuration.

    kubectl apply -f- <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: keycloak-agentregistry-realm
      namespace: keycloak
    data:
      agentregistry-realm.json: |
        {
          "realm": "agentregistry",
          "enabled": true,
          "clients": [
            {
              "clientId": "ar-backend",
              "enabled": true,
              "publicClient": false,
              "standardFlowEnabled": false,
              "directAccessGrantsEnabled": false
            },
            {
              "clientId": "ar-cli-interactive",
              "enabled": true,
              "publicClient": true,
              "standardFlowEnabled": false,
              "directAccessGrantsEnabled": false,
              "attributes": {
                "oauth2.device.authorization.grant.enabled": "true"
              },
              "protocolMappers": [
                {
                  "name": "groups",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-group-membership-mapper",
                  "consentRequired": false,
                  "config": {
                    "claim.name": "Groups",
                    "full.path": "false",
                    "id.token.claim": "true",
                    "access.token.claim": "true",
                    "userinfo.token.claim": "true"
                  }
                },
                {
                  "name": "ar-backend-audience",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-audience-mapper",
                  "consentRequired": false,
                  "config": {
                    "included.client.audience": "ar-backend",
                    "id.token.claim": "false",
                    "access.token.claim": "true"
                  }
                }
              ]
            },
            {
              "clientId": "ar-cli-password",
              "enabled": true,
              "publicClient": true,
              "standardFlowEnabled": false,
              "directAccessGrantsEnabled": true,
              "protocolMappers": [
                {
                  "name": "groups",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-group-membership-mapper",
                  "consentRequired": false,
                  "config": {
                    "claim.name": "Groups",
                    "full.path": "false",
                    "id.token.claim": "true",
                    "access.token.claim": "true",
                    "userinfo.token.claim": "true"
                  }
                },
                {
                  "name": "ar-backend-audience",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-audience-mapper",
                  "consentRequired": false,
                  "config": {
                    "included.client.audience": "ar-backend",
                    "id.token.claim": "false",
                    "access.token.claim": "true"
                  }
                }
              ]
            },
            {
              "clientId": "ar-ui",
              "enabled": true,
              "publicClient": true,
              "standardFlowEnabled": true,
              "directAccessGrantsEnabled": false,
              "redirectUris": ["*"],
              "webOrigins": ["*"],
              "attributes": {
                "pkce.code.challenge.method": "S256"
              },
              "protocolMappers": [
                {
                  "name": "groups",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-group-membership-mapper",
                  "consentRequired": false,
                  "config": {
                    "claim.name": "Groups",
                    "full.path": "false",
                    "id.token.claim": "true",
                    "access.token.claim": "true",
                    "userinfo.token.claim": "true"
                  }
                },
                {
                  "name": "ar-backend-audience",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-audience-mapper",
                  "consentRequired": false,
                  "config": {
                    "included.client.audience": "ar-backend",
                    "id.token.claim": "false",
                    "access.token.claim": "true"
                  }
                }
              ]
            },
            {
              "clientId": "ar-mcp-client",
              "enabled": true,
              "publicClient": true,
              "standardFlowEnabled": true,
              "directAccessGrantsEnabled": false,
              "redirectUris": ["http://localhost:*"],
              "webOrigins": ["http://localhost:*"],
              "attributes": {
                "pkce.code.challenge.method": "S256"
              },
              "protocolMappers": [
                {
                  "name": "groups",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-group-membership-mapper",
                  "consentRequired": false,
                  "config": {
                    "claim.name": "Groups",
                    "full.path": "false",
                    "id.token.claim": "true",
                    "access.token.claim": "true",
                    "userinfo.token.claim": "true"
                  }
                },
                {
                  "name": "ar-backend-audience",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-audience-mapper",
                  "consentRequired": false,
                  "config": {
                    "included.client.audience": "ar-backend",
                    "id.token.claim": "false",
                    "access.token.claim": "true"
                  }
                }
              ]
            }
          ],
          "groups": [
            {"name": "admins"}
          ],
          "users": [
            {
              "username": "admin-user",
              "email": "admin-user@example.com",
              "firstName": "Admin",
              "lastName": "User",
              "enabled": true,
              "credentials": [
                {"type": "password", "value": "password", "temporary": false}
              ],
              "groups": ["admins"]
            }
          ]
        }
    EOF

The following ConfigMap defines the resources for a setup where Solo Enterprise for agentregistry connects to Solo Enterprise for kagent. This configuration includes additional clients for the kagent management plane UI.

ResourceNameDescription
RealmagentregistryA dedicated Keycloak realm that isolates agentregistry’s identity configuration from other applications. The registry server uses the realm’s issuer URL to discover OIDC endpoints.
Clientar-backendA confidential client used by the registry server to validate access tokens issued by the CLI and UI clients.
Clientar-cli-interactiveA public client for the arctl CLI. Uses the device authorization grant (RFC 8628), which lets users log in from a terminal by opening a printed URL in a browser.
Clientar-cli-passwordA public client for scripted user authentication in CI/CD pipelines. Uses the password credentials grant with a username and password.
Clientar-uiA public client for the browser-based registry UI. Uses the authorization code flow with PKCE. The redirectUris and webOrigins are set to * for local testing. In production, replace these with the actual origin your UI uses.
Clientar-mcp-clientA public client for MCP clients such as Claude Code and Codex. Uses the authorization code flow with PKCE. Redirect URIs are restricted to http://localhost:* so that local MCP clients can receive the OAuth callback without using Dynamic Client Registration.
Clientkagent-backendA confidential client used by the kagent-enterprise controller and the solo-enterprise-ui backend to validate access tokens issued to kagent-ui. Tokens that include ar-backend in their aud claim are not accepted — only tokens with kagent-backend in the audience are.
Clientkagent-uiA public client for the solo-enterprise-ui browser frontend. Uses the authorization code flow with PKCE.
Protocol mappergroupsAn oidc-group-membership-mapper that writes the user’s Keycloak group memberships into the Groups JWT claim. Added to access tokens issued for ar-cli-interactive, ar-cli-password, ar-ui, ar-mcp-client, and kagent-ui clients.
Protocol mapperar-backend-audienceAn oidc-audience-mapper that adds ar-backend to the aud claim of access tokens issued for ar-cli-interactive, ar-cli-password, ar-ui, and ar-mcp-client clients. Without the correct aud claim, the registry server rejects tokens with a 401 Unauthorized error.
Protocol mapperkagent-backend-audienceAn oidc-audience-mapper that adds kagent-backend to the aud claim of access tokens issued for the kagent-ui client. Without this, kagent-enterprise rejects tokens from the UI with a 401 Unauthorized error.
ClientagentregistryA confidential service-account client that the agentregistry server uses to authenticate itself when calling the Solo Enterprise for kagent controller. When you create, update, or delete a deployment that targets the kagent runtime, the agentregistry server obtains an access token from Keycloak via the OAuth2 client_credentials grant using this client. The client has a groups protocol mapper that adds the service account’s group memberships to the Groups claim so that the kagent controller can map it to an RBAC role.
GroupadminsA group that grants full admin access in the registry. Assigned to admin-user so they have superuser rights on first login.
GroupagentregistryAssigned to the service-account-agentregistry user. The kagent RBAC maps membership in this group to the global.Writer role, which grants the agentregistry server permission to create, update, and delete kagent resources.
Useradmin-userA test admin user with the password password, assigned to the admins group.
Userservice-account-agentregistryA backing user that Keycloak automatically creates to represent the agentregistry client internally. Assigned to the agentregistry group at import time so that tokens minted via the client_credentials grant carry the Groups: ["agentregistry"] claim.
  1. Create the Keycloak namespace.

    kubectl create namespace keycloak
  2. Create the ConfigMap with your Solo Enterprise for agentregistry OIDC configuration.

    kubectl apply -f- <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: keycloak-agentregistry-realm
      namespace: keycloak
    data:
      agentregistry-realm.json: |
        {
          "realm": "agentregistry",
          "enabled": true,
          "accessTokenLifespan": 86400,
          "ssoSessionIdleTimeout": 86400,
          "ssoSessionMaxLifespan": 86400,
          "clients": [
            {
              "clientId": "ar-backend",
              "enabled": true,
              "publicClient": false,
              "standardFlowEnabled": false,
              "directAccessGrantsEnabled": false
            },
            {
              "clientId": "ar-cli-interactive",
              "enabled": true,
              "publicClient": true,
              "standardFlowEnabled": false,
              "directAccessGrantsEnabled": false,
              "attributes": {
                "oauth2.device.authorization.grant.enabled": "true"
              },
              "protocolMappers": [
                {
                  "name": "groups",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-group-membership-mapper",
                  "consentRequired": false,
                  "config": {
                    "claim.name": "Groups",
                    "full.path": "false",
                    "id.token.claim": "true",
                    "access.token.claim": "true",
                    "userinfo.token.claim": "true"
                  }
                },
                {
                  "name": "ar-backend-audience",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-audience-mapper",
                  "consentRequired": false,
                  "config": {
                    "included.client.audience": "ar-backend",
                    "id.token.claim": "false",
                    "access.token.claim": "true"
                  }
                }
              ]
            },
            {
              "clientId": "ar-cli-password",
              "enabled": true,
              "publicClient": true,
              "standardFlowEnabled": false,
              "directAccessGrantsEnabled": true,
              "protocolMappers": [
                {
                  "name": "groups",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-group-membership-mapper",
                  "consentRequired": false,
                  "config": {
                    "claim.name": "Groups",
                    "full.path": "false",
                    "id.token.claim": "true",
                    "access.token.claim": "true",
                    "userinfo.token.claim": "true"
                  }
                },
                {
                  "name": "ar-backend-audience",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-audience-mapper",
                  "consentRequired": false,
                  "config": {
                    "included.client.audience": "ar-backend",
                    "id.token.claim": "false",
                    "access.token.claim": "true"
                  }
                }
              ]
            },
            {
              "clientId": "ar-ui",
              "enabled": true,
              "publicClient": true,
              "standardFlowEnabled": true,
              "directAccessGrantsEnabled": false,
              "redirectUris": ["*"],
              "webOrigins": ["*"],
              "attributes": {
                "pkce.code.challenge.method": "S256"
              },
              "protocolMappers": [
                {
                  "name": "groups",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-group-membership-mapper",
                  "consentRequired": false,
                  "config": {
                    "claim.name": "Groups",
                    "full.path": "false",
                    "id.token.claim": "true",
                    "access.token.claim": "true",
                    "userinfo.token.claim": "true"
                  }
                },
                {
                  "name": "ar-backend-audience",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-audience-mapper",
                  "consentRequired": false,
                  "config": {
                    "included.client.audience": "ar-backend",
                    "id.token.claim": "false",
                    "access.token.claim": "true"
                  }
                }
              ]
            },
            {
              "clientId": "ar-mcp-client",
              "enabled": true,
              "publicClient": true,
              "standardFlowEnabled": true,
              "directAccessGrantsEnabled": false,
              "redirectUris": ["http://localhost:*"],
              "webOrigins": ["http://localhost:*"],
              "attributes": {
                "pkce.code.challenge.method": "S256"
              },
              "protocolMappers": [
                {
                  "name": "groups",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-group-membership-mapper",
                  "consentRequired": false,
                  "config": {
                    "claim.name": "Groups",
                    "full.path": "false",
                    "id.token.claim": "true",
                    "access.token.claim": "true",
                    "userinfo.token.claim": "true"
                  }
                },
                {
                  "name": "ar-backend-audience",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-audience-mapper",
                  "consentRequired": false,
                  "config": {
                    "included.client.audience": "ar-backend",
                    "id.token.claim": "false",
                    "access.token.claim": "true"
                  }
                }
              ]
            },
            {
              "clientId": "kagent-backend",
              "enabled": true,
              "publicClient": false,
              "standardFlowEnabled": false,
              "directAccessGrantsEnabled": false
            },
            {
              "clientId": "kagent-ui",
              "enabled": true,
              "publicClient": true,
              "standardFlowEnabled": true,
              "directAccessGrantsEnabled": false,
              "redirectUris": ["*"],
              "webOrigins": ["*"],
              "attributes": {
                "pkce.code.challenge.method": "S256"
              },
              "protocolMappers": [
                {
                  "name": "groups",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-group-membership-mapper",
                  "consentRequired": false,
                  "config": {
                    "claim.name": "Groups",
                    "full.path": "false",
                    "id.token.claim": "true",
                    "access.token.claim": "true",
                    "userinfo.token.claim": "true"
                  }
                },
                {
                  "name": "kagent-backend-audience",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-audience-mapper",
                  "consentRequired": false,
                  "config": {
                    "included.client.audience": "kagent-backend",
                    "id.token.claim": "false",
                    "access.token.claim": "true"
                  }
                }
              ]
            },
            {
              "clientId": "agentregistry",
              "enabled": true,
              "publicClient": false,
              "serviceAccountsEnabled": true,
              "standardFlowEnabled": false,
              "directAccessGrantsEnabled": false,
              "protocolMappers": [
                {
                  "name": "groups",
                  "protocol": "openid-connect",
                  "protocolMapper": "oidc-group-membership-mapper",
                  "consentRequired": false,
                  "config": {
                    "claim.name": "Groups",
                    "full.path": "false",
                    "id.token.claim": "false",
                    "access.token.claim": "true",
                    "userinfo.token.claim": "false"
                  }
                }
              ]
            }
          ],
          "groups": [
            {"name": "admins"},
            {"name": "agentregistry"}
          ],
          "users": [
            {
              "username": "admin-user",
              "email": "admin-user@example.com",
              "firstName": "Admin",
              "lastName": "User",
              "enabled": true,
              "credentials": [
                {"type": "password", "value": "password", "temporary": false}
              ],
              "groups": ["admins"]
            },
            {
              "username": "service-account-agentregistry",
              "enabled": true,
              "serviceAccountClientId": "agentregistry",
              "groups": ["agentregistry"]
            }
          ]
        }
    EOF

Step 2: Deploy Keycloak

With the realm configuration in place, deploy Keycloak. This step creates the Keycloak pod and exposes it through a load balancer service so that both the registry server and the arctl CLI can reach it. The --import-realm flag instructs Keycloak to load the ConfigMap from Step 1 on startup to apply all the realm, client, and user configuration automatically.

  1. Create the Keycloak deployment and service.

    kubectl apply -f- <<EOF
    apiVersion: v1
    kind: Service
    metadata:
      name: keycloak
      namespace: keycloak
      labels:
        app: keycloak
    spec:
      ports:
      - name: http
        port: 8080
        targetPort: 8080
      selector:
        app: keycloak
      type: LoadBalancer
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: keycloak
      namespace: keycloak
      labels:
        app: keycloak
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: keycloak
      template:
        metadata:
          labels:
            app: keycloak
        spec:
          containers:
          - name: keycloak
            image: quay.io/keycloak/keycloak:26.1.3
            args: ["start-dev", "--import-realm"]
            env:
            - name: KEYCLOAK_ADMIN
              value: "admin"
            - name: KEYCLOAK_ADMIN_PASSWORD
              value: "admin"
            - name: PROXY_ADDRESS_FORWARDING
              value: "true"
            - name: KC_PROXY
              value: "edge"
            ports:
            - name: http
              containerPort: 8080
            readinessProbe:
              httpGet:
                path: /realms/master
                port: 8080
            volumeMounts:
            - name: realm-config
              mountPath: /opt/keycloak/data/import
          volumes:
          - name: realm-config
            configMap:
              name: keycloak-agentregistry-realm
    EOF
  2. Wait for the Keycloak rollout to finish.

    kubectl -n keycloak rollout status deploy/keycloak
  3. Set the Keycloak endpoint details from the load balancer service. These environment variables are used throughout the rest of this guide. The KEYCLOAK_ISSUER is the OIDC discovery URL that the registry uses to find the token and JWKS endpoints. If you are running locally in Kind and need a local IP address for the load balancer service, consider using cloud-provider-kind.

    export ENDPOINT_KEYCLOAK=$(kubectl -n keycloak get service keycloak -o jsonpath='{.status.loadBalancer.ingress[0].ip}{.status.loadBalancer.ingress[0].hostname}'):8080
    export HOST_KEYCLOAK=$(echo ${ENDPOINT_KEYCLOAK} | cut -d: -f1)
    export PORT_KEYCLOAK=$(echo ${ENDPOINT_KEYCLOAK} | cut -d: -f2)
    export KEYCLOAK_URL=http://${ENDPOINT_KEYCLOAK}
    export KEYCLOAK_REALM=agentregistry
    export KEYCLOAK_ISSUER=$KEYCLOAK_URL/realms/${KEYCLOAK_REALM}
    echo $KEYCLOAK_ISSUER
  4. Get the client secrets that Keycloak automatically generated. You pass these secrets to the respective Helm charts in the next steps.

    Get the ar-backend client secret. The registry server uses this secret to verify access tokens.

    export KEYCLOAK_TOKEN=$(curl -s \
      -d "client_id=admin-cli" -d "username=admin" -d "password=admin" \
      -d "grant_type=password" \
      "${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" \
      | jq -r .access_token)
    
    export AR_BACKEND_ID=$(curl -s -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" \
      "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients?clientId=ar-backend" \
      | jq -r '.[0].id')
    
    export AR_BACKEND_SECRET=$(curl -s -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" \
      "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients/${AR_BACKEND_ID}/client-secret" \
      | jq -r .value)
    echo "AR_BACKEND_SECRET: $AR_BACKEND_SECRET"

    Get the ar-backend, kagent-backend, and agentregistry client secrets. The registry server uses the ar-backend secret to verify access tokens. The kagent-enterprise controller uses the kagent-backend secret to verify tokens issued to the kagent-ui client. The agentregistry secret is used by the registry server when it calls Solo Enterprise for kagent with its own service identity.

    export KEYCLOAK_TOKEN=$(curl -s \
      -d "client_id=admin-cli" -d "username=admin" -d "password=admin" \
      -d "grant_type=password" \
      "${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" \
      | jq -r .access_token)
    
    export AR_BACKEND_ID=$(curl -s -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" \
      "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients?clientId=ar-backend" \
      | jq -r '.[0].id')
    export AR_BACKEND_SECRET=$(curl -s -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" \
      "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients/${AR_BACKEND_ID}/client-secret" \
      | jq -r .value)
    echo "AR_BACKEND_SECRET: $AR_BACKEND_SECRET"
    
    export KAGENT_BACKEND_ID=$(curl -s -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" \
      "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients?clientId=kagent-backend" \
      | jq -r '.[0].id')
    export KAGENT_BACKEND_SECRET=$(curl -s -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" \
      "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients/${KAGENT_BACKEND_ID}/client-secret" \
      | jq -r .value)
    echo "KAGENT_BACKEND_SECRET: $KAGENT_BACKEND_SECRET"
    
    export AGENTREGISTRY_OUTBOUND_ID=$(curl -s -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" \
      "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients?clientId=agentregistry" \
      | jq -r '.[0].id')
    export AGENTREGISTRY_OUTBOUND_SECRET=$(curl -s -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" \
      "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients/${AGENTREGISTRY_OUTBOUND_ID}/client-secret" \
      | jq -r .value)
    echo "AGENTREGISTRY_OUTBOUND_SECRET: $AGENTREGISTRY_OUTBOUND_SECRET"

Next steps