Overview

Miru natively supports the use of JSON Schema to specify config schemas used to validate config instances before deployment. However, some config instances may require more complex, application-specific validation logic to ensure valid configurations are deployed. Miru provides webhooks and APIs, allowing you to host a custom validation server for validating config instances before deployment to your devices. Upon any new config instance deployments made in the UI (or otherwise), Miru will send the config_instance.target_status.validated webhook to your endpoint(s). The config instance must then be approved or rejected by your validation server using the approve or reject APIs. After the config instance has been approved, the validation server can deploy the config instance to it’s target device using the deploy API.

Enable Custom Validation

When custom validation is disabled, Miru first validates config instances against their config schemas on creation. If a config instance satisfies its schema, it is then immediately queued for deployment to its target device. Enabling custom validation adds an additional step to this process. Config instances must still satisfy their schema to be created, but must also await approval from your validation server before deployment. Custom validation is enabled or disabled on a per-config type basis. By default, custom validation is disabled for all config types. To enable custom validation for a config type, navigate to the Config Types page. Click the ellipses on the far right of the config type you would like to enable and select Edit from the dropdown. Toggle the webhook validation setting to enable custom validation for the config type.

Local Validation Server

Webhooks rely on a web server provisioned by you to receive and process the webhook events. We’ll walk through the setup of a simple server using Python and Flask. Begin by cloning the getting-started repository.
git clone https://github.com/miruml/getting-started.git
Navigate to the Python server-sdk directory inside the repository.
cd getting-started/python/server-sdk
Inside the flask directory you’ll find a simple flask application (app.py) which demonstrates webhook verification using the Miru server-side SDK. To run the server, create a virtual environment and install the dependencies.
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -r requirements.txt
Then run the server.
cd flask
python3 app.py
Verify the server is running by going to http://localhost:5000 in your browser. You should see {"message":"ok"}

Forward a Domain with ngrok

Miru webhooks don’t support sending requests to your local machine directly. All requests must be sent to valid domains over HTTPS. To test Miru webhooks locally, we’ll need to setup tunneling with ngrok. This will provision a temporary domain that forwards requests to your local machine, a useful method for locally testing webhooks.
  1. Navigate to ngrok and create an account
  2. Install ngrok to your machine according to their installation instructions and add your authoken to the ngrok configuration file.
  3. Forward an ephemeral domain to your Flask application over on port 5000
    ngrok http http://localhost:5000
    
    You should see a domain similar to https://c1971566f336.ngrok-free.app being forward to your Flask server.
As before, you can verify the server is running by going to https://c1971566f336.ngrok-free.app (you’ll need to replace c1971566f336 with your own ngrok host) in your browser. You should see {"message":"ok"}.

Create a Webhook Endpoint

For our server to receive webhooks, we’ll need to create an endpoint in Miru. Navigate to the webhooks page in Miru. Click Add Endpoint in the top right of the page. Copy your ephemeral ngrok domain with the /webhooks/miru route appended (e.g. https://c1971566f336.ngrok-free.app/webhooks/miru) to the webhook endpoint in the dashboard. Subscribe to the config_instance.target_status.validated event and click Create. On the far right of the page, you’ll see the webhook secret. Copy this value and store it in a secure location for use in your validation server. For simplicity the flask server uses the SECRET as a globally defined variable but you should not do this in practice. Webhook secrets should be stored in a secrets manager or other secure location, not inside your codebase.

Send an Example Webhook

To send an example webhook to your validation server, select the Testing tab under your webhook endpoint. In the middle of the page, you’ll see a Send Example button. Select the config_instance.target_status.validated event and click Send Example. If your secret is key is invalid or not specified, you’ll see an error message in Miru for that request similar to:
{"message":"No matching signature found"}
If your server is properly configured, you’ll see a response in miru for that request similar to:
{"message":"webhook successfully received"}

Trigger the Webhook

To trigger a real webhook, you’ll need to deploy a config instance to a device. To do this, navigate to the Devices page in Miru. Either provision a new device with config instances or select an existing device to deploy a config instance to.
Remember to deploy a config instances with a config type that has custom validation enabled. If custom validation is disabled, the config_instance.target_status.validated webhook will not be triggered.
Upon deployment of a config instance, you’ll notice the config instance is in the validating state, indicating that it is awaiting approval from your validation server before deployment to it’s target device.

Create an API Key

To approve the config instance, you’ll need to leverage the approve API. However, we’ll need to create an API key to authenticate the request. Navigate to the API Keys page in Miru. Click New API Key in the top right of the page. Copy the API key and store it in a secure location for use in your validation server.
API Keys are only available upon creation. If you lose your API key, you must create a new one.

Approve the Config Instance

After receiving the config_instance.target_status.validated webhook, you’ll need to approve the config instance using the approve API. The approve API stores a success message field and moves the config instance’s activity_status to be validated. You can approve the config instance by making the appropriate POST request in the API Playground. We recommend making a dedicated docs-api-key for this purpose which you delete after you’re done testing. Alternatively, if the config instance is invalid you can use the reject API to reject the config instance. This will move the config instance’s error_status to be failed, preventing any deployments to it’s target device. The reject API stores a failure message field as well as a list of parameter errors that caused the validation to fail. Be sure to give a descriptive message and errors, as these will be displayed in the UI for rejected config instances.

Deploy the Config Instance

Finally, with the config instance approved, you can deploy the config instance to it’s target device using the deploy API. The config instance must have been approved by your validation server before it can be deployed to it’s target device. If the config instance has not been approved, the deploy API will return an error stating that the config instance is not in a valid state for deployment.