Skip to main content

Local development

How to build, run and verify OpenJCockpit from source. For contribution guidelines see CONTRIBUTING.md.

Requirements​

  • JDK 25
  • Maven 3.9+
  • Node.js 22 and npm, only for the dashboard, which lives in the separate OpenJCockpit-frontend repository
  • Docker with Docker Compose, for running the full stack
  • A local Ollama installation with qwen3.6:27b, see LLM providers

Repository layout​

services/
ai-control-service/ Spring Boot: dashboard API, projects, credentials, spec queue, git-workspace jobs
embabel-agent-service/ Spring Boot: workflows, agent catalogue, tenant bindings, policy, approval gates (management plane)
embabel-agent-runtime/ Spring Boot + Embabel: agent execution, LLMs, agent tools, inbound A2A (execution plane)
git-mcp-server/ Spring AI MCP server: Git and GitHub tools
service-common/ Shared Java code
agents/ Local agent catalogue (Markdown agent definitions)
infrastructure/ Keycloak realm + theme, LiteLLM, OPA policies, Postgres init, seeds, stubs
shared/contracts/ OpenAPI contract (openapi.yaml)
scripts/ Stack start/stop/reset helpers, A2A roundtrip
docs-site/ Static documentation site (GitHub Pages)
docs/readme/ This documentation

Developer profile: start, reset, stop​

The local stack is the developer profile: Spring profile local, seeded local users and public placeholder secrets, demo projects and the spec-repository stub. It needs no production credentials (only an OPENAI_API_KEY and a host Ollama, see the Quickstart).

cp .env.example .env # once; set OPENAI_API_KEY
./scripts/stack-start.sh # build + start everything (idempotent); same as: docker compose up -d --build
./scripts/stack-stop.sh # stop; all state is kept
docker compose down # remove containers; named volumes are kept
docker compose down -v # DESTRUCTIVE: also deletes every volume (fresh start next time)

Opt-in: AGENTIC_CONTAINER_ENABLED=true docker compose --profile docker-jobs up -d gives ai-control-service access to the host Docker daemon for job containers (root-equivalent; see Security considerations).

Production uses different files entirely (deploy/, Spring profile prod); see the Production runbook.

Running parts of the stack​

Frontend (separate repository)​

The dashboard and its federated remotes live in OpenJCockpit-frontend, checked out next to this repository (../OpenJCockpit-frontend). The two stacks are independent Compose projects that find each other through ports published on the host: this stack publishes Keycloak on 8080 and ai-control-service on 9080; the frontend publishes the dashboard on 4000 and the remotes on 4403-4406.

# this repository
docker compose up -d --build # backend stack (or only: docker compose up -d postgres keycloak ai-control-service)

# OpenJCockpit-frontend
docker compose up -d --build # production-like dashboard + remotes in containers, proxying /api to :9080
# or, for development with hot reload (Vite proxies /api to http://localhost:9080):
npm install && npm run start:hub

The containerised dashboard and the Vite dev server both bind port 4000 (a registered Keycloak redirect URI), so run only one of them. The dashboard calls the backend APIs, so start ai-control-service (and what it depends on) for anything beyond the login and landing hub. ./scripts/stack-start.sh --infra-only starts only postgres and keycloak and prints the extra command to run ai-control-service locally.

The dashboard is one shell app (apps/dashboard) composing feature libraries under libs/ (api, project, skills-hub, spec-queue, spec-files, workflow, plus auth, ui, landing-hub). Boundaries between them are enforced by @nx/enforce-module-boundaries. Each library has its own README, for example frontend/libs/workflow/README.md and frontend/libs/ui/README.md (the self-authored animated/framed UI); see also frontend/apps/dashboard/README.md.

Running a Java service on the host​

A host-run embabel-agent-runtime (for example mvn -pl services/embabel-agent-runtime spring-boot:run) uses http://localhost:11434 for Ollama by default, and stores workflow definitions in ${java.io.tmpdir}/embabel-workflow-definitions rather than the Compose volume. See LLM providers and Runbooks.

Verification​

Backend verification (multi-module Maven, from the repo root):

mvn test
mvn verify

# or just this module:
mvn -pl services/embabel-agent-service -am test
mvn -pl services/embabel-agent-service -am verify
mvn -pl services/embabel-agent-runtime -am verify

mvn verify includes the JaCoCo line-coverage gate enforced in CI.

Frontend verification runs in the OpenJCockpit-frontend repository (see its README).

shared/contracts/openapi.yaml is owned by this repository, but the frontend generates its TypeScript types from it. After changing the contract, run npx nx run api:generate in the frontend checkout (it reads ../OpenJCockpit/shared/contracts/openapi.yaml) and commit the result there. The frontend's CI fails when its generated types are stale against this repository's main.

Compose configuration check (what the infra CI leg runs):

docker compose --env-file .env.example config

End-to-end tests: see Testing.

Running the agent service and the agent runtime​

docker compose up starts both as separate containers: embabel-agent-service on host port 8091 and embabel-agent-runtime on host port 8092. They start independently (neither waits for the other); until the runtime is reachable, local agents and definition drafts fail with "agent runtime unavailable".

To run one of them on the host against the rest of the stack, point it at the other by URL:

# runtime on the host, agent service in Compose
EMBABEL_AGENT_SERVICE_BASE_URL=http://localhost:8091 \
AGENT_RUNTIME_TOKEN_URL=http://localhost:8080/realms/openjcockpit/protocol/openid-connect/token \
AGENT_RUNTIME_CLIENT_SECRET=openjcockpit-agent-runtime-local-secret \
KEYCLOAK_JWK_SET_URI=http://localhost:8080/realms/openjcockpit/protocol/openid-connect/certs \
AGENT_DEFINITIONS_LOCAL_ROOT=services/embabel-agent-runtime/agent-definitions \
mvn -pl services/embabel-agent-runtime spring-boot:run

and set EMBABEL_AGENT_RUNTIME_BASE_URL=http://host.docker.internal:8082 for the agent service container.

Synced from OpenJCockpit@main. To change this page, edit it in the product repository.