Skip to main content

Local Environment Setup

This guide walks you through setting up the ILM platform locally using Docker Compose. By the end, you will have a fully running platform with all core services, a registered administrator, a working UI at localhost:5173, and a verified API connection — ready for writing automation tests.

Prerequisites

Make sure the following are installed before you begin:

  • Docker Desktopdocker.com/products/docker-desktop. After installing, start it and wait until the Docker icon in the system tray stops animating.
  • Git — verify with git --version in the terminal.
  • Node.js 18+ — verify with node --version. Download from nodejs.org.

Verify Docker is running:

docker ps

If you see an error about the Docker daemon, Docker Desktop is not started yet.

Step 1 — Create a working directory and clone repositories

Create a directory to hold all required repositories:

mkdir ~/ilm-local && cd ~/ilm-local

Clone the environment configuration repository:

git clone https://github.com/OmniTrustILM/development-environment.git

Clone the three services that are built from source:

git clone https://github.com/OmniTrustILM/auth.git
git clone https://github.com/OmniTrustILM/auth-opa-policies.git
git clone https://github.com/OmniTrustILM/scheduler.git

Clone the Administrator frontend:

git clone https://github.com/OmniTrustILM/fe-administrator.git

Your directory structure should look like this:

ilm-local/
├── development-environment/
├── auth/
├── auth-opa-policies/
├── scheduler/
└── fe-administrator/

All other services (Core, RabbitMQ, OPA, PostgreSQL) use pre-built public images and do not require source repositories.

Step 2 — Configure environment variables

cd ~/ilm-local/development-environment
cp .env.example .env

Open .env in any text editor and update CZERTAINLY_SOURCES_BASE_DIR to point to your working directory:

# macOS: /Users/yourname/ilm-local
# Linux: /home/yourname/ilm-local
# WSL: /home/yourname/ilm-local
CZERTAINLY_SOURCES_BASE_DIR=/Users/yourname/ilm-local

All other values can be left at their defaults when using PostgreSQL in Docker.

note

On macOS, files starting with . are hidden in Finder. Use Cmd + Shift + . to toggle their visibility, or edit them from the terminal.

Step 3 — Set up the trusted certificates file

The development-environment repository ships with a secrets/trusted_certificates.pem file that contains a legacy CZERTAINLY Root CA — this is not the CA used to sign the dummy administrator certificate from OmniTrustILM/helm-charts. You need to overwrite it with the ILM Dummy Root CA, otherwise the Auth service will reject the dummy administrator certificate with User client certificate is invalid.

curl -s https://raw.githubusercontent.com/OmniTrustILM/helm-charts/main/dummy-certificates/certs/root-ca.cert.pem \
> secrets/trusted_certificates.pem
important

Use > (overwrite), not >> (append). Appending leaves the legacy CA in the file and produces a malformed PEM concatenation, which breaks authentication later in Step 7.

Step 4 — Start the platform

docker compose -f czertainly-compose.yml -f postgres-compose.yml \
--profile database --profile core up --build

The --build flag tells Docker to build the three source-based services on first run. This takes 10–20 minutes the first time. Subsequent starts are much faster.

To run in the background (detached mode):

docker compose -f czertainly-compose.yml -f postgres-compose.yml \
--profile database --profile core up --build -d

Step 5 — Verify all services are running

docker compose -f czertainly-compose.yml -f postgres-compose.yml ps

All services should show status healthy or Up:

ServiceHost portDescription
postgres2345PostgreSQL database
rabbitmq5672 / 15672Message queue (UI at localhost:15672, login: guest/guest)
opa8181Open Policy Agent
opa-bundles8101OPA policy bundle server
auth8100Authentication service
scheduler8102Task scheduler
core8280Core API

Confirm the Core API is up:

curl http://localhost:8280/api/v1/health/liveness
# Expected: {"status":"UP"}

Step 6 — Create the first administrator

The platform uses X.509 certificate-based authentication. To register the first administrator, use the Local API.

important

The Local API listens only on the container's internal port 8080 and requires no authentication. The externally-mapped port 8280 exposes the regular API, which requires client-cert auth and returns HTTP 401 without one. Use docker exec to call the Local API from inside the container.

Save the administrator payload to a file:

cat > /tmp/first-admin.json << 'EOF'
{
"username": "admin",
"firstname": "Admin",
"lastname": "Local",
"email": "admin@local.test",
"certificateData": "REPLACE_WITH_CERT_BASE64",
"enabled": "true",
"description": "First Administrator"
}
EOF

Get the dummy administrator certificate (base64-encoded DER):

curl -s https://raw.githubusercontent.com/OmniTrustILM/helm-charts/main/dummy-certificates/certs/admin.cert.pem \
| grep -A 999 "BEGIN CERTIFICATE" | grep -v "BEGIN\|END" | tr -d '\n' \
> /tmp/admin_cert_b64.txt

Update the payload with the certificate value:

CERT_B64=$(cat /tmp/admin_cert_b64.txt)
sed -i.bak "s|REPLACE_WITH_CERT_BASE64|$CERT_B64|" /tmp/first-admin.json

Copy the file into the container and register the administrator:

docker cp /tmp/first-admin.json core:/tmp/first-admin.json

docker exec core curl -s -X POST \
-H 'content-type: application/json' \
-d @/tmp/first-admin.json \
http://localhost:8080/api/v1/local/admins

A successful response returns a JSON object with the administrator's uuid and role superadmin.

Step 7 — Verify API authentication

The platform authenticates API requests using the ssl-client-cert header. The certificate value must be URL-encoded — plain Base64 will fail because + characters are interpreted as spaces.

Generate the URL-encoded certificate:

CERT_URL=$(node -e "const fs=require('fs');console.log(encodeURIComponent(fs.readFileSync('/tmp/admin_cert_b64.txt','utf8').trim()))")

Verify authentication:

curl -s http://localhost:8280/api/v1/auth/profile \
-H "ssl-client-cert: $CERT_URL"

A successful response returns your administrator profile with role superadmin and a full list of permissions.

Step 8 — Start the Administrator frontend

The frontend is a separate Vite dev server that proxies API requests to the Core backend.

Install dependencies:

cd ~/ilm-local/fe-administrator
npm install

Create the proxy configuration file at src/setupProxy.js. This tells Vite to forward all /api requests to the Core backend and authenticate them with the dummy administrator certificate:

curl -s https://raw.githubusercontent.com/OmniTrustILM/helm-charts/main/dummy-certificates/certs/admin.cert.pem \
| grep -A 999 "BEGIN CERTIFICATE" | grep -v "BEGIN\|END" | tr -d '\n' \
> /tmp/admin_cert_b64.txt

CERT_URL=$(node -e "const fs=require('fs');console.log(encodeURIComponent(fs.readFileSync('/tmp/admin_cert_b64.txt','utf8').trim()))")

cat > src/setupProxy.js << EOF
const proxyConfig = {
server: {
proxy: {
'/api': {
target: 'http://localhost:8280',
changeOrigin: true,
secure: false,
headers: {
'ssl-client-cert': '${CERT_URL}',
},
},
},
},
};

export default proxyConfig;
EOF

Start the frontend:

npm start

The browser will open automatically at http://localhost:5173. Log in — the dummy administrator certificate is already injected via the proxy, so no login form is required.

important

The src/setupProxy.js file is gitignored. Each developer creates their own local copy with their certificate. Do not commit this file.

Step 9 — Stop the platform

Stop the frontend with Ctrl+C in its terminal.

Stop all backend services:

cd ~/ilm-local/development-environment

docker compose -f czertainly-compose.yml -f postgres-compose.yml \
--profile database --profile core down

Data in the database is persisted in the ./data/ directory. To reset the platform to a clean state, remove this directory before the next start.

Troubleshooting

SymptomCauseFix
docker ps fails with daemon errorDocker Desktop not startedStart Docker Desktop and wait for the icon to stop animating
docker compose up fails on secrets mountsecrets/trusted_certificates.pem does not existRun touch secrets/trusted_certificates.pem
Auth returns User client certificate is invalid (unable to get local issuer certificate)secrets/trusted_certificates.pem does not contain the ILM Dummy Root CA, or was appended to instead of overwrittenRe-run the curl from Step 3 with > (overwrite). Verify only one cert with grep -c "BEGIN CERTIFICATE" secrets/trusted_certificates.pem — should return 1. Then restart auth: docker compose ... restart auth
Local API returns HTTP 401 from hostPort 8280 is the regular API requiring cert auth; Local API is on container-internal port 8080 onlyUse docker exec core curl ... instead of calling localhost:8280 directly
Authentication returns Wrong format of user authentication certificateCertificate not URL-encodedUse node -e "console.log(encodeURIComponent('<base64_cert>'))" to URL-encode the certificate before sending
CZERTAINLY_SOURCES_BASE_DIR not foundWrong path in .envSet the full absolute path to the directory containing auth, auth-opa-policies, scheduler
Frontend shows blank page or API errorssetupProxy.js missing or wrong certRecreate src/setupProxy.js following Step 8
Frontend port 5173 already in useAnother Vite process runningKill it with lsof -ti:5173 | xargs kill