Skip to content

Devices Simulator Setup

The devices simulator is an optional platform component, disabled by default. This page walks through the steps required to enable it in a Kamea deployment.

For an overview of how the simulator is wired into the platform (CI gating, Terraform module, Caddy routing, Socket.IO co-existence), see Devices Simulator.

Prerequisites

  • A working Kamea deployment with a shared App Service Plan (the simulator API piggy-backs on module.core-platform.azurerm_service_plan_webapp_plan).
  • Caddy already deployed as the reverse proxy in front of the platform.
  • The identity provider used by the management API (JWT_ISSUER, JWT_AUDIENCE, JWT_ISSUER_WELL_KNOWN_URL) — the simulator API reuses the same JWT settings.
  • A user account on the identity provider with permission to call the simulator API.

Step 1 — Enable the USE_SIMULATOR CI variable

In the target environment's GitLab CI/CD variables (Settings → CI/CD → Variables), set:

USE_SIMULATOR = "true"

Scope it to the environment (or environments) that should run the simulator. Leave it false (or unset) elsewhere.

This single flag enables:

  • the simulator-back test, build, and deploy jobs;
  • the simulator-front build and deploy jobs;
  • the Terraform module.simulator instantiation, which creates the ${PROJECT_NAME}-simulator-api App Service.

Step 2 — Provide the simulator front-end environment file

The simulator front-end's build pipeline copies an environment file from $SPA_SIMULATOR_ENVIRONMENT_FILE_PATH (a path on the CI runner, typically a file delivered through a GitLab file CI variable) into core/apps/devices-simulator/front-end/environments/environment-simulator.values.json at build time.

The template at core/apps/devices-simulator/front-end/environments/environment-simulator.template.json documents the required fields. For a Caddy-fronted deployment the values should be:

json { "simulatorHttpBaseUrl": "/simulator-api", "simulatorWsPath": "/simulator-api/socket.io", "defaultMqttEndpoint": "mqtts://<your-mqtt-broker>:8883" }

  • simulatorHttpBaseUrl becomes the base URL prefixed to every REST call from the SPA. A relative path keeps the front-end same-origin with Caddy.
  • simulatorWsPath is passed verbatim to socket.io-client as its path option, so the WebSocket handshake hits the same Caddy reverse-proxy block.
  • defaultMqttEndpoint is the value prefilled in device-credentials forms when creating simulated devices.

Save this JSON somewhere accessible to the GitLab runner and set SPA_SIMULATOR_ENVIRONMENT_FILE_PATH to its path on the runner.

The companion file SPA_ENVIRONMENT_FILE_PATH (referenced by other front-ends as well) provides the Kamea-wide values (apiUrl, wssUrl, B2C client ID, etc.) and is reused unchanged.

Step 3 — Update the Caddy extension

The default core/apps/caddy/caddyfile.extension is intentionally minimal and does not route the simulator. Use the example overlay at core/apps/caddy/caddyfile.extension.simulator.example as the starting point:

  1. Copy the example to a project-specific location alongside the other Caddy extensions you maintain.
  2. Replace <project-name> with the actual PROJECT_NAME of your deployment, so the hostname matches the App Service created by Terraform (${PROJECT_NAME}-simulator-api.azurewebsites.net).
  3. Set the CI variable CADDY_EXTENSION_PATH in the target environment to the path of this file. The caddy:deploy job will copy it into the Caddy image at build time.

After the next Caddy deploy, the platform exposes:

  • https://<reverse-proxy>/simulator/ — the simulator SPA.
  • https://<reverse-proxy>/simulator-api/... — REST endpoints and Socket.IO handshakes routed to the simulator API.

Step 4 — Configure the identity provider for the simulator SPA

The simulator SPA reuses the platform's identity provider configuration from $SPA_ENVIRONMENT_FILE_PATH (the same one the management front-end uses), so no separate B2C/Auth0 app registration is strictly required. If your access-control policy requires the simulator SPA to be a distinct OAuth client, provision a new app registration in your IdP and override clientId / scope in the env file for this front-end.

The simulator API itself does not require its own app registration — it validates incoming JWTs using JWT_ISSUER, JWT_AUDIENCE, and JWT_ISSUER_WELL_KNOWN_URL, which are inherited from the platform-wide settings via Terraform.

Step 5 — Trigger a deployment

Run a deploy pipeline (push to the deploy branch, or trigger from the GitLab UI). On a successful run you should see:

  • simulator-back:deploy-dev — built the simulator API Docker image.
  • simulator-front:build and simulator-front:deploy — built the SPA and synced it to the frontend file share at frontend/simulator/.
  • terraform:apply — created (or updated) the ${PROJECT_NAME}-simulator-api Linux Web App and pointed it at the new image tag.
  • caddy:deploy — rebuilt the Caddy image with the new extension.

Step 6 — Verify

  1. Browse to https://<reverse-proxy>/simulator/. The simulator SPA should load and prompt for authentication.
  2. Sign in with a platform user account.
  3. Create a simulated device. The SPA's network panel should show REST calls hitting /simulator-api/... and a Socket.IO WebSocket connection at /simulator-api/socket.io/?.... Both should return successfully.
  4. Start the simulated device and verify telemetries appear on the management front-end.

Disabling the simulator

To remove the simulator from a deployment:

  1. Set USE_SIMULATOR = "false" in the environment's CI/CD variables.
  2. Re-run the deploy pipeline. The simulator CI jobs will be skipped, and terraform:apply will destroy module.simulator's resources (the App Service and any IP restrictions).
  3. Optionally, revert CADDY_EXTENSION_PATH to a Caddy extension that does not include the simulator routes, so the public surface no longer advertises /simulator/ and /simulator-api/.

Caveats

  • simulator-back:test-e2e:build uses a RabbitMQ service container. If USE_MQTT=false, the corresponding rabbitmq-server-e2e image is not built and the simulator's e2e test will fail to start. This coupling is not specific to the simulator's gating — it is the same dependency the platform-wide tests have on RabbitMQ.