1 - Getting Started

How to get started with maiLink SRM.

maiLink SRM is a cloud-based software platform that helps you manage, service and learn from your products in the field.

1.1 - Defined Terms

Defined terms often used in maiLink SRM.

We use the following terminology throughout the maiLink software and documentation:

Term Meaning
Agent A maiLink software module that is installed into each product in the field.
Client A maiLink software module that a service technician will install on his/her local computer. When the Client software runs it enables the users’s maiLink portal to build tunnels that give the user secure remote access to a selected device in the field.
Condition A logical expression that is evaluated to determine if a workflow is to be triggered.
Device One instance of a product in the field.
Device Tag A key/value pair that defines one parameter for one device.
Device Type A deprecated name for a model.
Model A class of devices in which the devices share common characteristics. Usually a model describes one manufactured model of products, so all the devices are basically the same. Devices of the same model will have some differences, such as dates of manufacture and different serial numbers.
Model Tag A key/value pair that defines one parameter that is common for all devices of the same model.
Tag A key/value pair that defines one parameter. The term is used in association with models and devices. See Device Tag and Model Tag.
Trigger The selected type of occurence that will initiate a workflow. At present the choices are: manual, event, metric and status.
Workflow A user-defined automation process that will perform a set of steps. The workflow configuration includes the trigger type, model, name and condition, if applicable.

1.2 - Setting Up maiLink

Setting up maiLink SRM for your company.

Before setting up maiLink, it’s useful to think through what your team will need to accomplish with maiLink.

  • The team will use maiLink SRM software to.
    • Manage your fleet of devices that are deployed in the field (and used in your internal development labs, too).
    • Perform service on those devices.
    • Monitor performance of those devices.
  • The fleet is comprised of products that are different models.
    • All the devices of a model have similar characteristics.
    • The methods you use to access devices of that model are shared by all those devices.
  • Each device will have unique characteristics.
    • The most important is the device ID, which must be unique for your company, across all models.

Part 1: Add the Team

  1. Identify the individuals that need access to maiLink SRM to do their jobs. For security, only add the people that really need access to maiLink.
  2. Identify what permissions each user really needs. For security, only give peoples the roles they need to accomplish their work.
  3. Add each team member, defining the roles for every person. See Add User.

Part 2: Add your Connection Types

  1. Navigate to Settings (Gear) > Connection Types.
  2. Review all the predefined connection types (the name and port)
  3. If a connection type is to be added, click Create Connection Type.
    1. Enter the name, port number and a connection string.
    2. Click Create.
  4. If a connection type needs changing, click Edit on the corresponding row.
    1. Review the name, the port number and the connection string.
    2. Change anything you like and click Update.

Connection String

The connection string is a shorthand that can contain the port number. When you create a connection, the string is presented to you in an expanded form with {{port}} replaced by the port number. If the resulting string is a valid URL, it is presented as a link, to make connection in a browser window even faster.

Consider the following examples whent the port number is 12345:

Connection String Expanded Connection String Valid URL
{{port}} 12345 No
localhost:{{port}}/ localhost:12345/ No
http://localhost:{{port}}/ http://localhost:12345/ Yes
http://localhost:{{port}}/nr http://localhost:12345/nr Yes

Part 3: Add the Models

  1. Navigate to Models on the top bar.
  2. To add a model:
    1. Click Create Model.
    2. Enter the name for the model. Short and clear is best.
    3. Click in the Connection Types pull-down and click every connection type that applies to this model.
    4. Add model tags by clicking the plus (+) and then entering the tag name and value.
    5. Click Create.

Part 4: Add the Devices

Note: Before adding the devices, take note that each device will be given a unique device ID. The device ID must be unique within your company, including unique across all models. You cannot have a Model 100 device with serial number 12345 and also have a Model 200 device with the same serial number. Plan ahead to avoid creating a lot of rework.

To add a device:

  1. Navigate to Devices on the top bar.
  2. Click Create Device.
  3. Enter the name for the device. Short and clear is best.
  4. Enter the device ID, which should be a unique identifier such as serial number.
  5. Add model tags by clicking the plus (+) and then entering the tag name and value.
  6. Click Create.

2 - How To

How to get basic tasks done with maiLink SRM.

Use the following guides to get set up with maiLink.

2.1 - Alarms

maiLink uses alarms to help users identify problems with devices in the field. Alarms are set by workflows, and once set, can be see in different places in the user interface.

You team can define different alarms for different purposes. An example might be the following set of four alarms:

Alarm Purpose
Error 1000 Pump pressure has exceeded 500 psi.
Error 1013 Pump pressure has exceeded 600 psi.
Error 2107 Filters need cleaning (insufficient airflow).
Warning 2107 Filters need cleaning (excessive hours in use).

Note: Alarms span workflows, meaning that multiple workflows can set the same alarm.
Note: Alarms can also span models, although maiData discourages that.

  • Caution: It is strongly recommended that you do not create alarms for use on multiple models.
  • Caution: A future release will include further information in the definition of an alarm that will make sharing alarms across models undesireable.

Alarms

Displaying Alarms

Alarms are displayed to users in several places:

  1. Click on the Devices selection in the maiLink UI top bar.
    • Any device (row) with an Alarm icon (🔔) has alarms that are currently set.
    • The number of alarms is shown with that icon.
  2. Navigate to a Devices > Device page.
    • In the lefthand menu the Alarms side tab selection may show an Alarm icon (🔔).
    • It will be displayed if any alarms are set for this device.
    • The number of alarms currently set is shown with that icon.
  3. Navigate to a Devices > Device > Alarms page.
    • The table indicates a history of all the alarms that have been set.
    • Active alarms are shown with the Alarm icon (🔔).
    • The number of alarms since the first alarm, or since the alarms were last cleared is also shown.

Defining Alarms

Alarms are a simple concept. Each has a name and a description. To create Alarms:

  1. Navigate to Settings (⚙).
  2. Select Alarm Definitions on the lefthand tabs.
  3. Click on Create Alarm to add a new alarm. Make sure the name of the alarm is unique.

Setting and Clearing Alarms

Setting Alarms

Alarms are set by workflows. Within a workflow you can add a Set an Alarm workflow step. You will be asked to select the name of the Alarm that you want to set from the list of Alarm Definitions. When the workflow is triggered, when it reaches the Set an Alarm step

  1. Navigate to Settings (⚙).
  2. Select Alarm Definitions on the lefthand tabs.
  3. Click on Create Alarm to add a new alarm. Make sure the name of the alarm is unique.

Clearing Alarms

Clearing alarms is the way that service technicians can indicate that resolution has been acheived. This is not a substitute for your standard service processes for documentation, but it does let clean up the user interface for the next service shift.

  1. Navigate to Devices > Device > Alarms.
  2. On a row with active alarms, click the Clear button.

Resetting Alarms

Resetting alarms may seem a little odd, but it is meant for the situation where perhaps something was not resolved as well as once thought, so the service technician wants to flag it for the next team.

  1. Navigate to Devices > Device > Alarms.
  2. On a row with cleared alarms, click the Reset button.

2.2 - Manage Devices

Specific tasks for managing devices.

2.2.1 - Add Linux Device

Install the maiLink Agent on a Linux-based system.

Prerequisites


  1. You have administrator rights to install software

Step 1: Begin installing Agent software on remote device


  1. In a browser, log into your maiLink account at app.maidata.io.
  2. Navigate to the Software tab.
  3. Locate the proper version, for example 64bit linux, mailink-agent_{version}_linux_amd64.deb, right click and copy the Link/URL.
  4. Go to your Terminal window and navigate to your home directory run the following command. Your coppied url will vary.
wget https://github.com/maiData/releases/releases/download/{version}/mailink-agent_{version}_linux_amd64.deb
  1. Run the following command (replacing version) in the directory you have transfered the .deb file to.
sudo apt install ./mailink-agent_{version}_linux_amd64.deb

Output

Reading package lists... Done
Building dependency tree
Reading state information... Done
Note, selecting 'mailink' instead of './mailink-agent_22.5.3_linux_amd64.deb'
The following NEW packages will be installed:
  mailink
0 upgraded, 1 newly installed, 0 to remove and 105 not upgraded.
After this operation, 13.8 MB of additional disk space will be used.
Get:1 /home/maidata/Downloads/mailink-agent_22.5.3_linux_amd64.deb mailink amd64 22.5.3 [5,537 kB]
Selecting previously unselected package mailink.
(Reading database ... 108002 files and directories currently installed.)
Preparing to unpack .../mailink-agent_22.5.3_linux_amd64.deb ...
 Please provide the device key:
 :>
Progress: [ 20%] [#####################.....................................................................................]
  1. Leave the installer as shown above and proceed to Step 2.

  1. Navigate to the Devices page.
  2. Click on Create Device.

  3. Enter a Device Name and Device ID (Serial Number is recommended).
  4. Click + to add tags that help identify the system, such as:
    1. Site Name
    2. Address
    3. IP Address

  5. Click Create to save the settings and open the Device Info page for the new device.

Step 3: Create a Device Key for the new device.


  1. Click on Device Keys side tab.

  2. Click on Create Key.

  3. Click on Copy Device Key to save the key to the Clipboard.

Step 4: Finish the Agent Installation


  1. Return to the Agent installer on the remote device.
  2. Paste the content of the clipboard (Right Click or Ctrl + Shift + V) into the Installer screen, and then hit .
  3. The installer will complete as follows…
Unpacking mailink-agent (22.9.11) ...
Setting up mailink-agent (22.9.11) ...
 Setting permissions
 Creating logging directory
 Creating linux configuration file
 Creating plugin directory structure
 Setting up service
  1. Verify the Agent is running by running this command
sudo systemctl status mailink-agent.service

Output

● mailink-agent.service - maiData maiLink Service
   Loaded: loaded (/etc/systemd/system/mailink-agent.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2022-09-19 20:18:02 UTC; 1min 29s ago
 Main PID: 7713 (mailink-agent)
    Tasks: 13 (limit: 500)
   CGroup: /system.slice/mailink-agent.service
           ├─7713 /usr/bin/mailink-agent run --mailink-home /etc/mailink
           └─7784 /etc/mailink/mailink-agent-plugin serve

Step 5: Verify the Agent is Connected


  1. Navigate to the Devices page in the maiLink webpage.
  2. Verify that the device is connected to the maiLink SRM Cloud by observing a small solid green dot in the upper left section of the “About Device” page.

Success!


The new device is now connected. If you have the maiLink Client installed on your machine, you will be able to access the new device remotely.

If you have issues, contact your Administrator or email maiData Customer Support at maiData at service@maidata.io.

2.2.2 - Add Windows Device

Install the maiLink Agent on a Windows-based system

Prerequisites


  1. You have installed maiLink Client on your computer
  2. You have downloaded or transferred the maiLink agent installer
  3. You have administrator rights to install software

Step 1: Begin installing Agent software on remote device


  1. In a browser, log into your maiLink account at app.maidata.io.
  2. Navigate to the Software tab.
  3. Select maiLink Agent and Windows.
  4. Click on the installer mailink-agent_{version}_windows_amd64.exe for the latest version of the Agent software.
  5. Follow the instructions until you arrive at the maiLink Agent Device key screen.

  6. Leave the installer as shown above and proceed to Step 2.

  1. Navigate to the Devices page.
  2. Click on Create Device.

  3. Enter a Device Name and Device ID (Serial Number is recommended).
  4. Click + to add tags that help identify the system, such as:
    1. Site Name
    2. Address
    3. IP Address

  5. Click Create to save the settings and open the Device Info page for the new device.

Step 3: Create a Device Key for the new device.


  1. Click on Device Keys side tab.

  2. Click on Create Key.

  3. Click on Copy Device Key to save the key to the Clipboard.

Step 4: Finish the Agent Installation


  1. Return to the Agent installer on the remote device.
  2. Click in the large installer text entry box and paste (Ctrl-V) the Device Key from the clipboard. Click Install.

  3. Then continue following the instructions until the installer completes.

Step 5: Verify the Agent is Connected


  1. Navigate to the Devices page in the maiLink webpage.
  2. Verify that the device is connected to the maiLink SRM Cloud by observing a small solid green dot at the left.

Success!


The new device is now connected. If you have the maiLink Client installed on your machine, you will be able to access the new device remotely.

If you have issues, contact your Administrator or email maiData Customer Support at maiData at service@maidata.io.

2.2.3 - Add Windows Client

Install the maiLink Client on a Windows-based system to gain secure remote access for service.

Prerequisites


  1. You have administrator rights to install software on the desired computer

  1. In a browser, log into your maiLink account at https://app.maidata.io.
  2. Navigate to the Software tab.
  3. Locate and download the Latest Client Software Version installer, the installer is the executable (.exe) file.
  4. The installer will have you register your client as part of the install steps, simply enter the Name you want as a reference for your client (e.g. Work laptop) and add a simple Description (e.g. HP Pavilion).

  5. complete the installation by returning to the installer window and clicking next and then Finish to complete the installation.

Success!


You can verify local client is registered by observing the green light indicator on the top right of the header!

If you have issues, contact your Administrator or email maiData Customer Support at maiData at service@maidata.io.

2.2.4 - Import Devices

How to Mass-Import Devices into maiLink.

This document tells you how prepare a CSV file for mass import into maiLink – it’s the quickest way to add a batch of new devices.

Note: The syntax of the CSV file matters, so please follow this guidance carefully.

Note: Once your CSV file is ready, contact support@maidata.io for help. Our team will upload the file for you. We offer this service as a protection for all our customers to prevent accidental SQL injection issues.

Creating a CSV File

Most users will create an Excel file containing the required data and then simply export it into a CSV file. The Excel file will be created by exporting data from some sort of asset manager, CRM system, or other database. Once the data is in Excel, it will be manipulated to meet the CSV Requirements below.

If you think your CSV file is ready, contact support@maidata.io.

CSV Requirements

The CSV file you create should have the following:

  1. Values seperated by commas (',').
  2. Any values containing commas enclosed in double quotes ('"').
  3. One header row.
  4. Any number of additional rows, where each row corresponds to one unique device.
  5. The following column headers in row 1:
Column Heading Cell Meaning
A Device ID A unique identifier for the device on this row. Should be globally unique within your organization. Often a serial number.
B Device Name A friendly name for this device.
C Model The type of device. Often the manufacturer’s name for the model.
D Address The address at which the devices in installed. Should include street address, city, state/province, postal/zip code, country. It is not necessary to put commas in between sections of the address. So, you can have 1 Able Street Missisauga ON Canada rather than 1 Able Street, Missisauga, ON, Canada.
E Device Tag 1 Key The value of a device tag that will be filled with the text given in column E for the device of a given row.
F Device Tag 2 Key The value of a device tag that will be filled with the text given in column F for the device of a given row.
G Device Tag 3 Key The value of a device tag that will be filled with the text given in column G for the device of a given row.
zz Device Tag xx Key The value of a device tag that will be filled with the text given in column zz for the device of a given row.

2.3 - Manage Models

How to manage models in maiLink SRM.

A model is a collection of devices that share most common attributes. Some examples of models are:

  • Model 3000 Digital Mammography System
  • Model 3000 Digital Breast Tomosynthesis System
  • Model 6000 Digital Breast Tomosynthesis System

The first two may share some common features, but have other differentiating features that would make the manufacturer want to keep track of them separately. Please think carefully when defining models to ensure you don’t have to change models going forward.

Add a Model

Note: To add a model you must have the Owner or Fleet Manager role assigned to you.

Adding a model takes a few steps:

  1. Create a model.
  2. Create a device.
  3. Configure telemetry charts.

** Note that you cannot define telemetry charts until you have device defined of that model type.

Create a Model

  1. Navigate to Models in the maiLink UI top bar.
  2. Click the Create button to begin defining the new model.
    • Enter the Name for the model.
    • Select the Connection Types that the devices of this model will support.
    • Next to the Tags label, click (+) to add a tag. Repeat as necessary.
  3. Click Save to store the model.

Note: You can’t define telemetry charts yet. First you must create a device.

Create a Device

Create a device of the model you defined above.

O/S Instructions
Linux Linux
Windows Windows

Configure Telemetry Charts

  1. Navigate to Models in the maiLink UI top bar.
  2. Hover over the model for which you will configure telemetry charts.
  3. Click on the Edit button edit the model.
  4. Under the Telemetry Charts section, pull down Example Device ID and select a device of this model.
  5. Next to Telemetry Charts click (+) to add a new telemetry chart.
    • Click in the New Chart and it will expand.
    • Type in a Name for the new chart.
    • Select the Period.
    • Select the Interval.
    • Click Add Query and:
      • If the device is connected and sending metrics:
        • Select a metric that is to be plotted on the chart, or
        • Type a query by hand into one of the Query boxes.
        • Examples:
          • md_memory_used_percent{device="$device.id"}
          • pump_pressure_mmHg{device="$device.id"}
          • disk_partition_free_bytes{device="$device.id",partition="/dev/sda1"}
      • If the device is not yet sending metrics:
        • Type a query by hand into one of the Query boxes.
    • Click Update.
    • When a dialog box appears, type update into the box and click update.

The charts you configure this way will appear in the About page for each device of this model. If you create a new device, it will inherit the telemetry charts defined for its model.

Edit a Model

  1. Navigate to Models in the maiLink UI top bar.
  2. Hover over the model for which you will configure telemetry charts.
  3. Click on the Edit button edit the model.
  4. Make the desired changes.
  5. Click Update.
    • When a dialog box appears, type update into the box and click update.

Delete a Model

Note: Deleting a model is a serious business because it will affect ALL devices that are of that model type. If you delete a model, all devices of that model type are set to “default” model type. There is no way to undo that change except for manually changing all those devices back, should you choose to undelete the model later on.

  1. Navigate to Models in the maiLink UI top bar.
  2. Hover over the model for which you will configure telemetry charts.
  3. Click on the Delete button to delete the model.
    • When a dialog box appears, type delete into the box and click delete.

2.4 - Manage Users

How to manage users and user permissions in maiLink SRM.

Add a User

Note: To add a user you must have the Owner or User Manager role assigned to you.

To add a user, you need to know that user’s full name and email address.

  1. Navigate to Settings (Gear) > Users & Access Control.
  2. Click on the Invite User button.
  3. Enter the person’s First Name, Last Name and Email.
  4. Select the Roles to assign to that person. Best practice is to only assign a person the roles they really need to get their work done.
  5. Click on the Send Invite button Send Invite is a misnomer today. Think of it instead as Add User.

Then email the following to the user:

- Please go to https://maidata.io.
- Enter your email address.
- Click on Forgot Password?
- Check your email ... there will be one from maidata that has a link for setting your password.
- Click that link.
- In the browser, maiLink will ask you to enter a password twice. Use a complex password:
    - At least 8 characters
    - At least one of !@#$%^&*
    - At least one uppercase and one lowercase character
    - At least one digit 0-9

Delete User

Note: To delete a user you must have the Owner or User Manager role assigned to you.

Note: You cannot delete yourself.

  1. Navigate to Settings (Gear) > Users & Access Control.
  2. On the row for the user that is to be deleted, click the Delete button.
  3. Confirm the action by entering the email of the user you are deleting.
  4. Click delete.

Set User Roles

Note: To set user roles you must have the Owner or User Manager role assigned to you. Note: You are not able to modify your own permissions.

You how make or modify the permissions that are assigned to a maiLink user. Permissions are how maiLink controls access to certain data or capabilities in the maiLink Cloud.

Assignment

mailink determines what data to show to a user, or what actions to allow that user to take, based on the roles that have been assigned to that user. The permissions assigned to a user can only be modified by a user with the USERS W permission.

  1. Navigate to Settings (Gear) > Users & Access Control.
  2. Select a user to modify by clicking Edit on that individual’s row.
  3. In the Permissions & Access Control section, pull down Roles.
  4. Toggle On / Off any of the roles that you want to add or remove.

Predefined Roles

The following roles are defined in maiLink.

Role Perform Actions For
Fleet Manager Managing models and devices in the maiLink database.
Manufacturing Specialist Adding devices to the maiLink database.
Owner Any maiLink administrative task required.
Service Specialist Servicing devices in the field, including establishing remote access sessions.
User Manager Modifying users (add/delete/edit), resetting passwords, and assigning roles to individual users.

Note: At this time it is not possible for administrators to modify these definitions, though that is being planned for a future release.

Role Permissions

Each permission is a combination of the TYPE of information to be accessed and the ACCESS granted. ACCESS can be R (read) or W (write). The following permissions (first two columns) are granted to each of the roles (latter columns).

TYPE ACCESS Fleet Manager Manufacturing Specialist Owner Service Specialist User Manager
Alarms R Yes Yes Yes Yes
Alarms W Yes Yes
Audit Logs R Yes Yes
Clients R Yes Yes
Clients W Yes Yes
Connection Types R Yes Yes Yes Yes
Connection Types W Yes Yes
Device Keys R Yes Yes Yes Yes
Device Keys W Yes Yes Yes Yes
Devices R Yes Yes Yes Yes
Devices W Yes Yes Yes
Device Types R Yes Yes Yes Yes
Device Types W Yes Yes
Metrics R Yes Yes Yes
Roles R Yes Yes
Roles W Yes Yes
Tunnels R Yes Yes
Tunnels W Yes Yes
Users R Yes Yes Yes Yes
Users W Yes Yes

2.5 - Test Telemetry

Experiment with sending Telemetry to the Agent REST API (Windows).

The maiLink Agent contains a REST API interface to which you can send Telemetry messages. Those messages are in the form of JSON data packets that must be formatted according to the maiLink Agent REST API Specification.

Getting Started

  1. Install the maiLink Agent on your computer using these instructions. It’s important that 1) the Agent is installed on the machine that will generate the telemetry, and 2) the Agent is up and running. You can check that everything is OK using the Windows Services tool (press Windows+R and type services.msc then press Enter) and scroll down to verify that maiLink Agent status is running.

  1. Open a PowerShell window, running it as administrator.
  2. Give yourself permission to run PowerShell scripts by entering the following command:
> Set-ExecutionPolicy unrestricted
  1. Create a working directory such as C:\Users\<your_name>\telemetry.
> cd ~
> md telemetry
> cd telemetry

■ Example: Sending Individual Metrics

The first PowerShell script accepts two command line parameters, creates a JSON data object, and pushes it to the Agent REST API.

  1. Open a new text file.
> notepad.exe sendMetric.ps1
  1. Copy and paste the following code into the editor, then save it.
# sendMetric.ps1 -- a simple Metric sender
#
# Usage: sendMetric [label] [value]

param(
    $label,
    $value
)

$json = "{""type"":""metric"",""label"":""" + $label + """,""value"":" + $value + "}"
$returnvalue = Invoke-RestMethod -Method Post -Uri "http://localhost:5465/mailink/v1/telemetry" -ContentType 'application/json' -Body $json
  1. Run the script a number of times, slightly changing the value while keeping the label gateTemp__C the same. This will start to build up data in the maiLink Telemetry Metrics database.
> .\sendMetric gateTemp__C 39.2
> .\sendMetric gateTemp__C 39.3
> .\sendMetric gateTemp__C 39.5
> .\sendMetric gateTemp__C 39.2
> .\sendMetric gateTemp__C 39.1
> .\sendMetric gateTemp__C 39.0
> .\sendMetric gateTemp__C 22.0
> .\sendMetric gateTemp__C 44.0

If your repeat this command a number of times, slightly changing the value while keeping the label gateTemp__C the same, you will start to build up data in the maiLink Telemetry Metrics database. Note that the time when you run the script defines the times associated with each data point – the Agent creates the timestamps for you.

JSON Data Object

The JSON data object for one of the single telemetry messages is simple:

{
    "type": "metric",
    "label": "<label>",
    "value": <value>
}

■ Example: Sending Batch Metrics

  1. Open a new text file.
> notepad.exe generateBatchMetrics.ps1
  1. Copy and paste the following code into generateMetrics.ps1 and save it.
# generateBatchMetrics.ps1 -- a maiLink Telemetry data generator
#
# Use this script to generate a number of geometric waveforms that are sent to the cloud, with a batch
# sent for each time point. The waveforms are:
#
#    Waveform  Description
#    --------  ---------
#    cos       Cosine
#    coshalf   Half-wave cosine
#    sin       Sine
#    sinhalf   Half-wave sine
#    tan       Tangent (clipped to +/- 100)


# Put all the functions requested into an array.
$functions = $("cos", "coshalf", "sin", "sinhalf", "tan")

$pi = [math]::PI
$delta = $pi / 72.   # For 10 cycles per hour (1 cycle every 360 seconds) need 72 samples at 5 second rate).
For ($t=0.0; $t -lt 10*$pi; $t+=$delta) {

    $json = "{""items"":["
    For ($l=0; $l -lt $functions.Length; $l++) {
        $label = $functions[$l]
        switch ($label) {
            "cos" {$value = [math]::cos($t)}
            "coshalf" {$value = [math]::abs([math]::cos($t))}
            "sin" {$value = [math]::sin($t)}
            "sinhalf" {$value = [math]::abs([math]::sin($t))}
            "tan" {$value = [math]::min([math]::max([math]::tan($t),-100.),100.)}
        }
        
        # Here is the content from sendMetric.ps1
        $json += "{""type"":""metric"",""label"":""" + $label + """,""value"":" + $value + "},"
    }
    $json = $json.Substring(0,$json.Length-1)
    $json += "]}"
    $returnvalue = Invoke-RestMethod -Method Post -Uri "http://localhost:5465/mailink/v1/telemetry/batch" -ContentType 'application/json' -Body $json

    Write-Host "." -NoNewline 
    Start-Sleep -Seconds 5.0
}
  1. Run the script. It generates a series of batch JSON data objects and sends one to the Cloud every 5 seconds. You only have to run it once … it will continue running for about an hour.
> .\sendBatchMetrics

You can visualize as many as four of the metrics at once. Just change the Query strings to any of these:

Metric Query String
cos cos{device=“device.id”}
coshalf coshalf{device=“device.id”}
sin sin{device=“device.id”}
sinhalf sinhalf{device=“device.id”}
tan tan{device=“device.id”}

JSON Data Object

The JSON data object for one of the batch telemetry messages looks like:

{
    "items": [
        {
            "type": "metric",
            "label": "cos",
            "value": <value>
        },
        {
            "type": "metric",
            "label": "coshalf",
            "value": <value>
        },
        {
            "type": "metric",
            "label": "sin",
            "value": <value>
        },
        {
            "type": "metric",
            "label": "sinhalf",
            "value": <value>
        },
        {
            "type": "metric",
            "label": "tan",
            "value": <value>
        }
    ]
}

Cleaning Up

At present there is no way to delete experimental telemetry data that you may have sent to the Cloud using the instructions above. Be aware that after 90 days all telemetry data ages out of the Cloud database and is thus lost. This includes both variable names and data points (if a variable name has not been transmitted to the Cloud with telemetry data in the last 90 days, it too is removed).

2.6 - Update Agent

Update the Agent on a Device.

The following methods are used to update the maiLink Agent on Devices without having to establish a connection and log in.

Single Device

To update the Agent version on a Device:

  1. Open the maiLink web app.
  2. Navigate to Devices > DeviceID.
  3. Select Update Device from the left hand tab bar.

Locate the Update Agent section, which shows the current Agent version.

  1. Click in the Versions pull-down and select one of the available versions.
  2. Click Apply to upgrade the Device to the selected version.

Note that the same controls can be used to revert to earlier versions of the Agent as well. Simply select an older version from the Available Versions drop-down and Apply. maiLink will not allow you to revert to versions of the Agent that were released prior to the release of Agent single-click upgrade because of software architecture incompatability.

Selected Devices

Future Enhancement

Device Type

Future Enhancement

3 - Reference

Detailed reference documentation about maiLink

3.1 - Agent API

Details about the REST API exposed by the maiLink Agent.

Telemetry

maiLink SRM supports a REST API interface in the Agent that accepts telemetry payloads as JSON messages using a REST API. The telemetry payloads come in two forms:

  • as a standard telemetry payload containing a single event
  • as a batch telemetry payload containing an array of distinct events

There is no implied relationship between the messages within a batch telemetry payload.

File Transfer

maiLink SRM supports a REST API interface in the Agent that enables transferring files to the cloud.

3.2 - Metrics

Detailed reference documentation about maiLink

3.2.1 - Metrics Explorer

Familiarize yourself with the Metrics Explorer.

The Metrics Explorer allows you to create useful time-sequence plots for evaluating metrics that your deployed devices send to the Cloud. The Metrics Explorer provides both the ability to select which metrics are to be uses, but also to preprocess the data using Query Functions and Query Operators, as well as alter the time period and sampling of data displayed.

Queries

To create a time-sequence plot in the Metrics Explorer in maiLink SRM you have to create a query in a language known as PromQL. maiLink SRM leverages VictoriaMetrics, which in turn relies on the Prometheus database. PromQL is the query language of Prometheus.

Raw Time-Sequence Queries

■ For a Specific Device

To plot the raw values (straight from the database) for a specified metric and a specified device, use a query that refers to the true Device ID:

my_metric{device="my_device_id"}
# example: heartbeat{device="123317-01A"}
■ For the Current Device

To plot the raw values (straight from the database) for a specified metric on the current device, use a query that subsitutes $device.id for the true Device ID (the current Device ID will be appropriately substituted when the query is performed):

my_metric{device="$device.id"}
# example: heartbeat{device="$device.id"}

Using $device.id lets a plot be created at the Device Type level and, when visualized for a specific device, have the data from this device plotted.

Accumulated Time-Sequence Queries

Calculations such as sums can be performed during the query in order to accumulate values over a time period. This helps reduce the number of data points and simplifies the plotting of data.

sum_over_time(my_metric{device="my_device_id"}[my_time_period])
# example: sum_over_time(ProbeTemp_C{device="$device.id"}[1m])

For the example shown above, assume a heartbeat comes every 10 seconds. The example queiry will create a value of about 6 because it is summing over a 1 minute time period (one should expect about 6 heartbeats to arrive at a rate of one every 10 seconds). Note that there may be some sampling error in this data (because of clock offsets), so you would get between 5 and 7 heartbeats per minute in reality.

■ Specifying Time Periods

Time period are specified using concatenated numbers and units, goverened by the regex expression [0-9]+([ywdhms]|ms) for each subsection. The units in each section should be one of:

Unit Description
ms milliseconds
s seconds
m minutes
h hours
d days (with 24 hours)
w weeks (with 7 days)
y years (with 365 days)

When periods are mixed, such as 1h30m, they should be constructed logically (largest units first).

Rolling Average of Accumulated Time-Sequence Queries

Rolling averages can be produced by averaging data over time. In the examples below tp represents a time period.

avg_over_time(sum_over_time(my_metric{device="my_device"}[tp1])[tp2])
# example: avg_over_time(sum_over_time(heartbeat{device="$device.id"}[1m])[2m30s])

In the above example the syntax of the function nesting means that the [1m] time period applies to the sum_over_time() function, and the [2m30s] time period applies to the avg_over_time() function. Also, simpler to the prior example, let’s assume the heartbeat comes every 10 seconds. As discussed above, the sum_over_time() value will be between 5 and 7. But if you do a rolling average using avg_over_time() you can expect the data to be smoothed to 6 heartbeats per minute, one every 10 seconds.

Additional Resources

Further information is available at:

3.2.2 - Query Basics

Familiarize yourself with the PromQL Query Syntax.

maiLink Telemetry supports the PromQL query syntax. Below is a basic guide to querying in maiLink Telemetry. Apply what you learn here in the maiLink Telemetry Metrics Explorer. This page is derived from PromQL documentation – it is possible to find the latest updates here.

maiLink Telemetry uses a functional query language called PromQL (Prometheus Query Language) that lets the user select and aggregate time series data in real time. The result of an expression is shown as a graph within maiLink SRM.

Expression language data types

In Prometheus’s expression language, an expression or sub-expression can evaluate to one of four types:

Type Description
Instant vector A set of time series containing a single sample for each time series, all sharing the same timestamp
Range vector A set of time series containing a range of data points over time for each time series
Scalar A simple numeric floating point value
String A simple string value; currently unused

Depending on the use-case (e.g. when graphing vs. displaying the output of an expression), only some of these types are legal as the result from a user-specified expression. For example, an expression that returns an instant vector is the only type that can be directly graphed.

Literals

■ String literals

Strings may be specified as literals in single quotes, double quotes or backticks.

PromQL follows the same escaping rules as Go. In single or double quotes a backslash begins an escape sequence, which may be followed by a, b, f, n, r, t, v or \. Specific characters can be provided using octal (\nnn) or hexadecimal (\xnn, \unnnn and \Unnnnnnnn).

No escaping is processed inside backticks. Unlike Go, PromQL does not discard newlines inside backticks.

Example:

"this is a string"
'these are unescaped: \n \\ \t'
`these are not unescaped: \n ' " \t`

■ Float literals

Scalar float values can be written as literal integer or floating-point numbers in the format (whitespace only included for better readability):

[-+]?(
    [0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?
  | 0[xX][0-9a-fA-F]+
  | [nN][aA][nN]
  | [iI][nN][fF]
)

Examples:

23
-2.43
3.4e-9
0x8f
-Inf
NaN

Time series Selectors

■ Instant vector selectors

Instant vector selectors allow the selection of a set of time series and a single sample value for each at a given timestamp (instant): in the simplest form, only a metric name is specified. This results in an instant vector containing elements for all time series that have this metric name.

This example selects all time series that have the http_requests_total metric name:

http_requests_total

It is possible to filter these time series further by appending a comma separated list of label matchers in curly braces ({}).

This example selects only those time series with the http_requests_total metric name that also have the job label set to prometheus and their group label set to canary:

http_requests_total{job="prometheus",group="canary"}

It is also possible to negatively match a label value, or to match label values against regular expressions. The following label matching operators exist:

Operator Description
=: Select labels that are exactly equal to the provided string.
!=: Select labels that are not equal to the provided string.
=~: Select labels that regex-match the provided string.
!~: Select labels that do not regex-match the provided string.

Regex matches are fully anchored. A match of env=~“foo” is treated as env=~"^foo$".

For example, this selects all http_requests_total time series for staging, testing, and development environments and HTTP methods other than GET.

http_requests_total{environment=~"staging|testing|development",method!="GET"}

Label matchers that match empty label values also select all time series that do not have the specific label set at all. It is possible to have multiple matchers for the same label name.

Vector selectors must either specify a name or at least one label matcher that does not match the empty string. The following expression is illegal:

{job=~".*"} # Bad!

In contrast, these expressions are valid as they both have a selector that does not match empty label values.

{job=~".+"}              # Good!
{job=~".*",method="get"} # Good!

Label matchers can also be applied to metric names by matching against the internal __name__ label. For example, the expression http_requests_total is equivalent to {__name__=“http_requests_total”}. Matchers other than = (!=, =~, !~) may also be used. The following expression selects all metrics that have a name starting with “job:":

{\_\_name__=~"job:.*"}

The metric name must not be one of the keywords bool, on, ignoring, group_left and group_right. The following expression is illegal:

on{} # Bad!

A workaround for this restriction is to use the __name__ label:

{__name__="on"} # Good!

All regular expressions in Prometheus use RE2 syntax.

■ Range Vector Selectors

Range vector literals work like instant vector literals, except that they select a range of samples back from the current instant. Syntactically, a time duration is appended in square brackets ([]) at the end of a vector selector to specify how far back in time values should be fetched for each resulting range vector element.

In this example, we select all the values we have recorded within the last 5 minutes for all time series that have the metric name http_requests_total and a job label set to prometheus:

http_requests_total{job=“prometheus”}[5m]

■ Time Durations

Time durations are specified as a number, followed immediately by one of the following units:

Duration Description
ms milliseconds
s seconds
m minutes
h hours
d days - assuming a day has always 24h
w weeks - assuming a week has always 7d
y years - assuming a year has always 365d

Time durations can be combined, by concatenation. Units must be ordered from the longest to the shortest. A given unit must only appear once in a time duration.

Here are some examples of valid time durations:

5h
1h30m
5m
10s

■ Offset modifier

The offset modifier allows changing the time offset for individual instant and range vectors in a query.

For example, the following expression returns the value of http_requests_total 5 minutes in the past relative to the current query evaluation time:

http_requests_total offset 5m

Note that the offset modifier always needs to follow the selector immediately, i.e. the following would be correct:

sum(http_requests_total{method="GET"} offset 5m) // GOOD.

While the following would be incorrect:

sum(http_requests_total{method="GET"}) offset 5m // INVALID.

The same works for range vectors. This returns the 5-minute rate that http_requests_total had a week ago:

rate(http_requests_total[5m] offset 1w)

For comparisons with temporal shifts forward in time, a negative offset can be specified:

rate(http_requests_total[5m] offset -1w)

Note that this allows a query to look ahead of its evaluation time.

■ @ modifier

The @ modifier allows changing the evaluation time for individual instant and range vectors in a query. The time supplied to the @ modifier is a unix timestamp and described with a float literal.

For example, the following expression returns the value of http_requests_total at 2021-01-04T07:40:00+00:00:

http_requests_total @ 1609746000

Note that the @ modifier always needs to follow the selector immediately, i.e. the following would be correct:

While the following would be incorrect:

sum(http_requests_total{method="GET"}) @ 1609746000 // INVALID.

The same works for range vectors. This returns the 5-minute rate that http_requests_total had at 2021-01-04T07:40:00+00:00:

rate(http_requests_total[5m] @ 1609746000)

The @ modifier supports all representation of float literals described above within the limits of int64. It can also be used along with the offset modifier where the offset is applied relative to the @ modifier time irrespective of which modifier is written first. These 2 queries will produce the same result.

# offset after @
http_requests_total @ 1609746000 offset 5m
# offset before @
http_requests_total offset 5m @ 1609746000

Additionally, start() and end() can also be used as values for the @ modifier as special values.

For a range query, they resolve to the start and end of the range query respectively and remain the same for all steps.

For an instant query, start() and end() both resolve to the evaluation time.

http_requests_total @ start()
rate(http_requests_total[5m] @ end())

Note that the @ modifier allows a query to look ahead of its evaluation time.

Subquery

Subquery allows you to run an instant query for a given range and resolution. The result of a subquery is a range vector.

Syntax: <instant_query> ‘[’ <range> ‘:’ [<resolution>] ‘]’ [ @ <float_literal> ] [ offset <duration> ]

  • <resolution> is optional. Default is the global evaluation interval.

Operators

Prometheus supports many binary and aggregation operators. These are described in detail in the expression language operators page.

Functions

Prometheus supports several functions to operate on data. These are described in detail in the expression language functions page.

Comments

PromQL supports line comments that start with #. Example:

# This is a comment

Gotchas

■ Staleness

When queries are run, timestamps at which to sample data are selected independently of the actual present time series data. This is mainly to support cases like aggregation (sum, avg, and so on), where multiple aggregated time series do not exactly align in time. Because of their independence, Prometheus needs to assign a value at those timestamps for each relevant time series. It does so by simply taking the newest sample before this timestamp.

If a target scrape or rule evaluation no longer returns a sample for a time series that was previously present, that time series will be marked as stale. If a target is removed, its previously returned time series will be marked as stale soon afterwards.

If a query is evaluated at a sampling timestamp after a time series is marked stale, then no value is returned for that time series. If new samples are subsequently ingested for that time series, they will be returned as normal.

If no sample is found (by default) 5 minutes before a sampling timestamp, no value is returned for that time series at this point in time. This effectively means that time series “disappear” from graphs at times where their latest collected sample is older than 5 minutes or after they are marked stale.

Staleness will not be marked for time series that have timestamps included in their scrapes. Only the 5 minute threshold will be applied in that case.

■ Avoiding slow queries and overloads

If a query needs to operate on a very large amount of data, graphing it might time out or overload the server or browser. Thus, when constructing queries over unknown data, always start building the query in the tabular view of Prometheus’s expression browser until the result set seems reasonable (hundreds, not thousands, of time series at most). Only when you have filtered or aggregated your data sufficiently, switch to graph mode. If the expression still takes too long to graph ad-hoc, pre-record it via a recording rule.

This is especially relevant for Prometheus’s query language, where a bare metric name selector like api_http_requests_total could expand to thousands of time series with different labels. Also keep in mind that expressions which aggregate over many time series will generate load on the server even if the output is only a small number of time series. This is similar to how it would be slow to sum all values of a column in a relational database, even if the output value is only a single number.

Additional Resources

Further information is available at:

3.2.3 - Query Examples

Some simple examples to help you understand Metrics Queries.

maiLink Telemetry supports the PromQL query syntax. Below is a set of examples of querying in maiLink Telemetry. Apply what you learn here in the maiLink Telemetry Metrics Explorer.

But First …

■ Specifying Devices

In maiLink Telemetry queries, you can use the device= syntax to specify the device context for plotting.

To display data for a specific device use:

deviceID="12345"

where “12345” is the unique Device Identifier for that device.

To display data for the “current” device use, and be able to use the very same query across other devices of the same type, use:

deviceID="$device.id"

■ Beware of Data Overload

The choices you make when querying data can impact performance. For instance, if you make the selected period too long, you may try and retrieve too much data and slow performance. If you make the sample rate too small, you may similarly try and retrieve too much data and slow performance.

■ Beware of Sampling Error

If you try and retreive too little data, you can run into other issues. For instance, if your selected period is too short, you might not observe important events within your data. Or, if you make the sample rate too long, then you might inadvertently wind up with aliasing, where you miss important details in the data.

Smoothing Data

You can easily smooth the data with functions like avg_over_time():

avg_over_time(OvenTemperature__C{device=$device.id})[5m]

In this instance the temperature is averaged over time, creating a rolling average of your temperature. This smooths the data to let you observe more general trends in the data. It gets rid of the “noise” by effectively applying a low-pass filter.

Query Examples

■ Example 1

Suppose you have a been sending a metric called “OvenTemperature__C” to maiLink Metrics. To plot the time series you can use the query:

OvenTemperature__C{device=$device.id}

This will display the raw data from the time series … for data over the selected period, sampled at the selected sample rate. Be aware that there is a balance you must make.

■ Example 2

Suppose you have a been sending a software heartbeat to maiLink SRM once per minute. In theory you should be receiving 60 heartbeats per hour. The raw matric you could plot is:

MySoftwareHeartbeat{device=$device.id}

This should plot as a horizontal line, which isn’t very interesting. Instead, perhaps you might plot the number of heartbeats received per hour. This would allow you to see if the rate ever changes, like taking the derivative of the data. You can do this by summing the data over a period of an hour:

sum_over_time(MySoftwareHeartbeat{device=$device.id})[1h]

Additional Resources

Further information is available at:

3.2.4 - Query Functions

This document provides comprehensive descriptions of all the built-in functions of PromQL.

maiLink Telemetry supports the PromQL query syntax, which uses the functions described below. This page is derived from PromQL documentation, which may be more recent here.

Some functions have default arguments, e.g. year(v=vector(time()) instant-vector). This means that there is one argument v which is an instant vector, which if not provided it will default to the value of the expression vector(time()).

Regular Functions

Regular functions operate on input vectors from the Telemetry database. See also Summary Functions and Trigonemetric Functions, below.

abs()

abs(v instant-vector) returns the input vector with all sample values converted to their absolute value.

absent()

absent(v instant-vector) returns an empty vector if the vector passed to it has any elements and a 1-element vector with the value 1 if the vector passed to it has no elements.

This is useful for alerting on when no time series exist for a given metric name and label combination.

absent(nonexistent{job="myjob"})
# => {job="myjob"}
absent(nonexistent{job="myjob",instance=~".*"})
# => {job="myjob"}
absent(sum(nonexistent{job="myjob"}))
\# => {}

In the first two examples, absent() tries to be smart about deriving labels of the 1-element output vector from the input vector.

absent_over_time()

absent_over_time(v range-vector) returns an empty vector if the range vector passed to it has any elements and a 1-element vector with the value 1 if the range vector passed to it has no elements.

This is useful for alerting on when no time series exist for a given metric name and label combination for a certain amount of time.

absent_over_time(nonexistent{job="myjob"}[1h])
# => {job="myjob"}
absent_over_time(nonexistent{job="myjob",instance=~".*"}[1h])
# => {job="myjob"}
absent_over_time(sum(nonexistent{job="myjob"})[1h:])
# => {}

In the first two examples, absent_over_time() tries to be smart about deriving labels of the 1-element output vector from the input vector.

ceil()

ceil(v instant-vector) rounds the sample values of all elements in v up to the nearest integer.

changes()

For each input time series, changes(v range-vector) returns the number of times its value has changed within the provided time range as an instant vector.

clamp()

clamp(v instant-vector, min scalar, max scalar) clamps the sample values of all elements in v to have a lower limit of min and an upper limit of max. Special cases are:

Returns an empty vector if min > max
Returns NaN if min or max is NaN

clamp_max()

clamp_max(v instant-vector, max scalar) clamps the sample values of all elements in v to have an upper limit of max.

clamp_min()

clamp_min(v instant-vector, min scalar) clamps the sample values of all elements in v to have a lower limit of min.

day_of_month()

day_of_month(v=vector(time()) instant-vector) returns the day of the month for each of the given times in UTC. Returned values are from 1 to 31.

day_of_week()

day_of_week(v=vector(time()) instant-vector) returns the day of the week for each of the given times in UTC. Returned values are from 0 to 6, where 0 means Sunday etc.

day_of_year()

day_of_year(v=vector(time()) instant-vector) returns the day of the year for each of the given times in UTC. Returned values are from 1 to 365 for non-leap years, and 1 to 366 in leap years.

days_in_month()

days_in_month(v=vector(time()) instant-vector) returns number of days in the month for each of the given times in UTC. Returned values are from 28 to 31.

delta()

delta(v range-vector) calculates the difference between the first and last value of each time series element in a range vector v, returning an instant vector with the given deltas and equivalent labels. The delta is extrapolated to cover the full time range as specified in the range vector selector, so that it is possible to get a non-integer result even if the sample values are all integers.

The following example expression returns the difference in CPU temperature between now and 2 hours ago:

delta(cpu_temp_celsius{host="zeus"}[2h])

delta should only be used with maiLink Telemetry metrics.

deriv()

deriv(v range-vector) calculates the per-second derivative of the time series in a range vector v, using simple linear regression.

deriv should only be used with maiLink Telemetry metrics.

exp()

exp(v instant-vector) calculates the exponential function for all elements in v. Special cases are:

Exp(+Inf) = +Inf
Exp(NaN) = NaN

floor()

floor(v instant-vector) rounds the sample values of all elements in v down to the nearest integer.

histogram_quantile()

histogram_quantile(φ scalar, b instant-vector) calculates the φ-quantile (0 ≤ φ ≤ 1) from the buckets b of a histogram. (See histograms and summaries for a detailed explanation of φ-quantiles and the usage of the histogram metric type in general.) The samples in b are the counts of observations in each bucket. Each sample must have a label le where the label value denotes the inclusive upper bound of the bucket. (Samples without such a label are silently ignored.) The histogram metric type automatically provides time series with the _bucket suffix and the appropriate labels.

Use the rate() function to specify the time window for the quantile calculation.

Example: A histogram metric is called http_request_duration_seconds. To calculate the 90th percentile of request durations over the last 10m, use the following expression:

histogram_quantile(0.9, rate(http_request_duration_seconds_bucket[10m]))

The quantile is calculated for each label combination in http_request_duration_seconds. To aggregate, use the sum() aggregator around the rate() function. Since the le label is required by histogram_quantile(), it has to be included in the by clause. The following expression aggregates the 90th percentile by job:

histogram_quantile(0.9, sum by (job, le) (rate(http_request_duration_seconds_bucket[10m])))

To aggregate everything, specify only the le label:

histogram_quantile(0.9, sum by (le) (rate(http_request_duration_seconds_bucket[10m])))

The histogram_quantile() function interpolates quantile values by assuming a linear distribution within a bucket. The highest bucket must have an upper bound of +Inf. (Otherwise, NaN is returned.) If a quantile is located in the highest bucket, the upper bound of the second highest bucket is returned. A lower limit of the lowest bucket is assumed to be 0 if the upper bound of that bucket is greater than 0. In that case, the usual linear interpolation is applied within that bucket. Otherwise, the upper bound of the lowest bucket is returned for quantiles located in the lowest bucket.

If b has 0 observations, NaN is returned. If b contains fewer than two buckets, NaN is returned. For φ < 0, -Inf is returned. For φ > 1, +Inf is returned. For φ = NaN, NaN is returned.

holt_winters()

holt_winters(v range-vector, sf scalar, tf scalar) produces a smoothed value for time series based on the range in v. The lower the smoothing factor sf, the more importance is given to old data. The higher the trend factor tf, the more trends in the data is considered. Both sf and tf must be between 0 and 1.

holt_winters should only be used with maiLink Telemetry metrics.

hour()

hour(v=vector(time()) instant-vector) returns the hour of the day for each of the given times in UTC. Returned values are from 0 to 23.

idelta()

idelta(v range-vector) calculates the difference between the last two samples in the range vector v, returning an instant vector with the given deltas and equivalent labels.

idelta should only be used with maiLink Telemetry metrics.

increase()

increase(v range-vector) calculates the increase in the time series in the range vector. Breaks in monotonicity (such as counter resets due to target restarts) are automatically adjusted for. The increase is extrapolated to cover the full time range as specified in the range vector selector, so that it is possible to get a non-integer result even if a counter increases only by integer increments.

The following example expression returns the number of HTTP requests as measured over the last 5 minutes, per time series in the range vector:

increase(http_requests_total{job="api-server"}[5m])

increase should only be used with counters. It is syntactic sugar for rate(v) multiplied by the number of seconds under the specified time range window, and should be used primarily for human readability. Use rate in recording rules so that increases are tracked consistently on a per-second basis.

irate()

irate(v range-vector) calculates the per-second instant rate of increase of the time series in the range vector. This is based on the last two data points. Breaks in monotonicity (such as counter resets due to target restarts) are automatically adjusted for.

The following example expression returns the per-second rate of HTTP requests looking up to 5 minutes back for the two most recent data points, per time series in the range vector:

irate(http_requests_total{job="api-server"}[5m])

irate should only be used when graphing volatile, fast-moving counters. Use rate for alerts and slow-moving counters, as brief changes in the rate can reset the FOR clause and graphs consisting entirely of rare spikes are hard to read.

Note that when combining irate() with an aggregation operator (e.g. sum()) or a function aggregating over time (any function ending in _over_time), always take a irate() first, then aggregate. Otherwise irate() cannot detect counter resets when your target restarts.

label_join()

For each timeseries in v, label_join(v instant-vector, dst_label string, separator string, src_label_1 string, src_label_2 string, …) joins all the values of all the src_labels using separator and returns the timeseries with the label dst_label containing the joined value. There can be any number of src_labels in this function.

This example will return a vector with each time series having a foo label with the value a,b,c added to it:

label_join(up{job="api-server",src1="a",src2="b",src3="c"}, "foo", ",", "src1", "src2", "src3")

label_replace()

For each timeseries in v, label_replace(v instant-vector, dst_label string, replacement string, src_label string, regex string) matches the regular expression regex against the value of the label src_label. If it matches, the value of the label dst_label in the returned timeseries will be the expansion of replacement, together with the original labels in the input. Capturing groups in the regular expression can be referenced with $1, $2, etc. If the regular expression doesn’t match then the timeseries is returned unchanged.

This example will return timeseries with the values a:c at label service and a at label foo:

label_replace(up{job="api-server",service="a:c"}, "foo", "$1", "service", "(.*):.*")

ln()

ln(v instant-vector) calculates the natural logarithm for all elements in v. Special cases are:

ln(+Inf) = +Inf
ln(0) = -Inf
ln(x < 0) = NaN
ln(NaN) = NaN

log2()

log2(v instant-vector) calculates the binary logarithm for all elements in v. The special cases are equivalent to those in ln.

log10()

log10(v instant-vector) calculates the decimal logarithm for all elements in v. The special cases are equivalent to those in ln.

minute()

minute(v=vector(time()) instant-vector) returns the minute of the hour for each of the given times in UTC. Returned values are from 0 to 59.

month()

month(v=vector(time()) instant-vector) returns the month of the year for each of the given times in UTC. Returned values are from 1 to 12, where 1 means January etc.

predict_linear()

predict_linear(v range-vector, t scalar) predicts the value of time series t seconds from now, based on the range vector v, using simple linear regression.

predict_linear should only be used with maiLink Telemetry metrics.

rate()

rate(v range-vector) calculates the per-second average rate of increase of the time series in the range vector. Breaks in monotonicity (such as counter resets due to target restarts) are automatically adjusted for. Also, the calculation extrapolates to the ends of the time range, allowing for missed scrapes or imperfect alignment of scrape cycles with the range’s time period.

The following example expression returns the per-second rate of HTTP requests as measured over the last 5 minutes, per time series in the range vector:

rate(http_requests_total{job="api-server"}[5m])<p>

rate should only be used with counters. It is best suited for alerting, and for graphing of slow-moving counters.

Note that when combining rate() with an aggregation operator (e.g. sum()) or a function aggregating over time (any function ending in _over_time), always take a rate() first, then aggregate. Otherwise rate() cannot detect counter resets when your target restarts.

resets()

For each input time series, resets(v range-vector) returns the number of counter resets within the provided time range as an instant vector. Any decrease in the value between two consecutive samples is interpreted as a counter reset.

resets should only be used with counters.

round()

round(v instant-vector, to_nearest=1 scalar) rounds the sample values of all elements in v to the nearest integer. Ties are resolved by rounding up. The optional to_nearest argument allows specifying the nearest multiple to which the sample values should be rounded. This multiple may also be a fraction.

scalar()

Given a single-element input vector, scalar(v instant-vector) returns the sample value of that single element as a scalar. If the input vector does not have exactly one element, scalar will return NaN.

sgn()

sgn(v instant-vector) returns a vector with all sample values converted to their sign, defined as this: 1 if v is positive, -1 if v is negative and 0 if v is equal to zero.

sort()

sort(v instant-vector) returns vector elements sorted by their sample values, in ascending order.

sort_desc()

Same as sort, but sorts in descending order.

sqrt()

sqrt(v instant-vector) calculates the square root of all elements in v.

time()

time() returns the number of seconds since January 1, 1970 UTC. Note that this does not actually return the current time, but the time at which the expression is to be evaluated.

timestamp()

timestamp(v instant-vector) returns the timestamp of each of the samples of the given vector as the number of seconds since January 1, 1970 UTC.

vector()

vector(s scalar) returns the scalar s as a vector with no labels.

year()

year(v=vector(time()) instant-vector) returns the year for each of the given times in UTC.

Summary Functions

Summary functions provide operations across a time series. The names are always appended with “_over_time”.

<aggregation>_over_time()

The following functions allow aggregating each series of a given range vector over time and return an instant vector with per-series aggregation results:

Function Description
avg_over_time(range-vector) The average value of all points in the specified interval.
min_over_time(range-vector) The minimum value of all points in the specified interval.
max_over_time(range-vector) The maximum value of all points in the specified interval.
sum_over_time(range-vector) The sum of all values in the specified interval.
count_over_time(range-vector) The count value of all values in the specified interval.
quantile_over_time(range-vector) The φ-quantile (0 ≤ φ ≤ 1) of the values in the specified interval.
stddev_over_time(range-vector) The population standard deviation of the values in the specified interval.
stdvar_over_time(range-vector) The population standard variance of the values in the specified interval.
last_over_time(range-vector) The most recent point value in specified interval
present_over_time(range-vector) The value 1 for any series in the specified interval.

Note that all values in the specified interval have the same weight in the aggregation even if the values are not equally spaced throughout the interval.

Trigonometric Functions

The trigonometric functions work in radians:

Function Description
acos(v instant-vector) Calculates the arccosine of all elements in v (special cases).
acosh(v instant-vector) Calculates the inverse hyperbolic cosine of all elements in v (special cases).
asin(v instant-vector) Calculates the arcsine of all elements in v (special cases).
asinh(v instant-vector) Calculates the inverse hyperbolic sine of all elements in v (special cases).
atan(v instant-vector) Calculates the arctangent of all elements in v (special cases).
atanh(v instant-vector) Calculates the inverse hyperbolic tangent of all elements in v (special cases).
cos(v instant-vector) Calculates the cosine of all elements in v (special cases).
cosh(v instant-vector) Calculates the hyperbolic cosine of all elements in v (special cases).
sin(v instant-vector) Calculates the sine of all elements in v (special cases).
sinh(v instant-vector) Calculates the hyperbolic sine of all elements in v (special cases).
tan(v instant-vector) Calculates the tangent of all elements in v (special cases).
tanh(v instant-vector) Calculates the hyperbolic tangent of all elements in v (special cases).

Angle Conversion Functions

The following additional functions are useful when working with Trigonometric Functions:

Function Description
deg(v instant-vector) Converts radians to degrees for all elements in v.
pi() Returns pi.
rad(v instant-vector) Converts degrees to radians for all elements in v.

Additional Resources

Further information is available at:

3.2.5 - Query Operators

Learn about the operators available in PromQL.

maiLink Telemetry supports the PromQL query syntax, which uses the operators described below. This page is derived from PromQL documentation, which may be more recent here.

Operators

There are several kinds of operators available in PromQL that support complex queries.

Binary operators

PromQL supports basic logical and arithmetic operators. For operations between two instant vectors, the matching behavior can be modified.

■ Arithmetic binary operators

The following binary arithmetic operators exist in Prometheus:

+   addition
-   subtraction
*   multiplication
/   division
%   modulo
^   power/exponentiation

Binary arithmetic operators are defined between scalar/scalar, vector/scalar, and vector/vector value pairs.

Between two scalars, the behavior is obvious: they evaluate to another scalar that is the result of the operator applied to both scalar operands.

Between an instant vector and a scalar, the operator is applied to the value of every data sample in the vector. E.g. if a time series instant vector is multiplied by 2, the result is another vector in which every sample value of the original vector is multiplied by 2. The metric name is dropped.

Between two instant vectors, a binary arithmetic operator is applied to each entry in the left-hand side vector and its matching element in the right-hand vector. The result is propagated into the result vector with the grouping labels becoming the output label set. The metric name is dropped. Entries for which no matching entry in the right-hand vector can be found are not part of the result.

■ Trigonometric binary operators

The following trigonometric binary operators, which work in radians, exist in Prometheus:

atan2   returns the arc tangent of y/x

Trigonometric operators allow trigonometric functions to be executed on two vectors using vector matching, which isn’t available with normal functions. They act in the same manner as arithmetic operators.

■ Comparison binary operators

The following binary comparison operators exist in Prometheus:

==   equal
!=   not-equal
>    greater-than
<    less-than
>=   greater-or-equal
<=   less-or-equal

Comparison operators are defined between scalar/scalar, vector/scalar, and vector/vector value pairs. By default they filter. Their behavior can be modified by providing bool after the operator, which will return 0 or 1 for the value rather than filtering.

Between two scalars, the bool modifier must be provided and these operators result in another scalar that is either 0 (false) or 1 (true), depending on the comparison result.

Between an instant vector and a scalar, these operators are applied to the value of every data sample in the vector, and vector elements between which the comparison result is false get dropped from the result vector. If the bool modifier is provided, vector elements that would be dropped instead have the value 0 and vector elements that would be kept have the value 1. The metric name is dropped if the bool modifier is provided.

Between two instant vectors, these operators behave as a filter by default, applied to matching entries. Vector elements for which the expression is not true or which do not find a match on the other side of the expression get dropped from the result, while the others are propagated into a result vector with the grouping labels becoming the output label set. If the bool modifier is provided, vector elements that would have been dropped instead have the value 0 and vector elements that would be kept have the value 1, with the grouping labels again becoming the output label set. The metric name is dropped if the bool modifier is provided.

■ Logical/set binary operators

These logical/set binary operators are only defined between instant vectors:

and     intersection
or       union
unless   complement

vector1 and vector2 results in a vector consisting of the elements of vector1 for which there are elements in vector2 with exactly matching label sets. Other elements are dropped. The metric name and values are carried over from the left-hand side vector.

vector1 or vector2 results in a vector that contains all original elements (label sets + values) of vector1 and additionally all elements of vector2 which do not have matching label sets in vector1.

vector1 unless vector2 results in a vector consisting of the elements of vector1 for which there are no elements in vector2 with exactly matching label sets. All matching elements in both vectors are dropped.

■ Binary operator precedence

The following list shows the precedence of binary operators in Prometheus, from highest to lowest.

  1. ^
  2. *, /, %, atan2
  3. +, -
  4. ==, !=, <=, <, >=, >
  5. and, unless
  6. or

Operators on the same precedence level are left-associative. For example, 2 * 3 % 2 is equivalent to (2 * 3) % 2. However ^ is right associative, so 2 ^ 3 ^ 2 is equivalent to 2 ^ (3 ^ 2).#### Vector matching operators Operations between vectors attempt to find a matching element in the right-hand side vector for each entry in the left-hand side. There are two basic types of matching behavior: One-to-one and many-to-one/one-to-many.

Vector matching operators

Operations between vectors attempt to find a matching element in the right-hand side vector for each entry in the left-hand side. There are two basic types of matching behavior: One-to-one and many-to-one/one-to-many.

■ One-to-one vector matches

One-to-one finds a unique pair of entries from each side of the operation. In the default case, that is an operation following the format vector1 <operator> vector2. Two entries match if they have the exact same set of labels and corresponding values. The ignoring keyword allows ignoring certain labels when matching, while the on keyword allows reducing the set of considered labels to a provided list:

<vector expr> <bin-op> ignoring(<label list>) <vector expr>
<vector expr> <bin-op> on(<label list>) <vector expr>

Example input:

method_code:http_errors:rate5m{method="get", code="500"} 24
method_code:http_errors:rate5m{method="get", code="404"} 30
method_code:http_errors:rate5m{method="put", code="501"} 3
method_code:http_errors:rate5m{method="post", code="500"} 6
method_code:http_errors:rate5m{method="post", code="404"} 21
method:http_requests:rate5m{method="get"} 600
method:http_requests:rate5m{method="del"} 34
method:http_requests:rate5m{method="post"} 120

Example query:

method_code:http_errors:rate5m{code="500"} / ignoring(code) method:http_requests:rate5m

This returns a result vector containing the fraction of HTTP requests with status code of 500 for each method, as measured over the last 5 minutes. Without ignoring(code) there would have been no match as the metrics do not share the same set of labels. The entries with methods put and del have no match and will not show up in the result:

{method="get"}  0.04            //  24 / 600
{method="post"} 0.05            //   6 / 120
■ Many-to-one and one-to-many vector matches

Many-to-one and one-to-many matchings refer to the case where each vector element on the “one”-side can match with multiple elements on the “many”-side. This has to be explicitly requested using the group_left or group_right modifier, where left/right determines which vector has the higher cardinality.

<vector expr> <bin-op> ignoring(<label list>) group_left(<label list>) <vector expr><p>
<vector expr> <bin-op> ignoring(<label list>) group_right(<label list>) <vector expr><p>
<vector expr> <bin-op> on(<label list>) group_left(<label list>) <vector expr><p>
<vector expr> <bin-op> on(<label list>) group_right(<label list>) <vector expr><p>

The label list provided with the group modifier contains additional labels from the “one”-side to be included in the result metrics. For on a label can only appear in one of the lists. Every time series of the result vector must be uniquely identifiable.

Note: Grouping modifiers can only be used for comparison and arithmetic. Operations as and, unless and or operations match with all possible entries in the right vector by default.

Example query:

method_code:http_errors:rate5m / ignoring(code) group_left method:http_requests:rate5m In this case the left vector contains more than one entry per method label value. Thus, we indicate this using group_left. The elements from the right side are now matched with multiple elements with the same method label on the left:

{method="get", code="500"}  0.04            //  24 / 600
{method="get", code="404"}  0.05            //  30 / 600
{method="post", code="500"} 0.05            //   6 / 120
{method="post", code="404"} 0.175           //  21 / 120

Many-to-one and one-to-many matching are advanced use cases that should be carefully considered. Often a proper use of ignoring() provides the desired outcome.

Aggregation operators

Prometheus supports the following built-in aggregation operators that can be used to aggregate the elements of a single instant vector, resulting in a new vector of fewer elements with aggregated values:

Operator Description
sum Calculate sum over dimensions
min Select minimum over dimensions
max Select maximum over dimensions
avg Calculate the average over dimensions
group All values in the resulting vector are 1
stddev Calculate population standard deviation over dimensions
stdvar Calculate population standard variance over dimensions
count Count number of elements in the vector
count_values Count number of elements with the same value. Outputs one time series per unique sample value. Each series has an additional label. The name of that label is given by the aggregation parameter, and the label value is the unique sample value. The value of each time series is the number of times that sample value was present.
bottomk Smallest k elements by sample value. Different from other aggregators in that a subset of the input samples, including the original labels, are returned in the result vector. by and without are only used to bucket the input vector.
topk Largest k elements by sample value. Different from other aggregators in that a subset of the input samples, including the original labels, are returned in the result vector. by and without are only used to bucket the input vector.
quantile Calculates the φ-quantile, the value that ranks at number φ*N among the N metric values of the dimensions aggregated over. φ is provided as the aggregation parameter. For example, quantile(0.5, …) calculates the median, quantile(0.95, …) the 95th percentile. For φ = NaN, NaN is returned. For φ < 0, -Inf is returned. For φ > 1, +Inf is returned.

These operators can either be used to aggregate over all label dimensions or preserve distinct dimensions by including a without or by clause. These clauses may be used before or after the expression.

<aggr-op> [without|by (<label list>)] ([parameter,] <vector expression>)

or

<aggr-op>([parameter,] <vector expression>) [without|by (<label list>)]

label list is a list of unquoted labels that may include a trailing comma, i.e. both (label1, label2) and (label1, label2,) are valid syntax.

without removes the listed labels from the result vector, while all other labels are preserved in the output. by does the opposite and drops labels that are not listed in the by clause, even if their label values are identical between all elements of the vector.

parameter is only required for count_values, quantile, topk and bottomk.

Example:

If the metric http_requests_total had time series that fan out by application, instance, and group labels, we could calculate the total number of seen HTTP requests per application and group over all instances via:

sum without (instance) (http_requests_total)

Which is equivalent to:

sum by (application, group) (http_requests_total)

If we are just interested in the total of HTTP requests we have seen in all applications, we could simply write:

sum(http_requests_total)

To count the number of binaries running each build version we could write:

count_values("version", build_version)

To get the 5 largest HTTP requests counts across all instances we could write:

topk(5, http_requests_total)

Additional Resources

Further information is available at:

3.3 - Workflows

Detailed reference documentation about maiLink Workflows.

3.3.1 - Overview

Familiarize yourself with maiLink Workflows.

Workflows are designed to automate a series of one or more steps that are to be taken given a specific trigger. That trigger may be manually invoked (by a person) or automatically invoked (by telemetry).

Each workflow has a definition with five main components, listed in the order they are defined here:

Component Description
Trigger The type of trigger. One of: Manual, Event, Metric, Status. Once this is defined, you cannot change it.
Model The model to which the workflow applies.
Name The name of the workflow.
Condition Only applies to Event, Metric and Status triggers. The condition is a logical expression that will be evaluated each time a telemetry message is received to determine if the workflows should be triggered.
Steps Steps are individual work items that must be performed in sequence to accomplish the goal(s) of the workflow.

Workflow Execution

Workflows, whether initiated manually or automatically, are executed in the cloud. The cloud processes each initiated workflow one step at a time. It starts a step, and waits for a result from that step before proceeding. If the step fails, the workflow is terminated. If the step succeeds then the cloud initiates processing of the next step.

Steps are processed either in the cloud or on the device, depending on what the step is trying to accomplish. In general, file transfers are managed by the maiLink agent on the device, as are device-side command, script and executable processing steps. The cloud generally handles distribution of tasks to devices as well as all direct communication steps between maiLink and 3rd party APIs.

Execution Sequence

Every time a workflow gets triggered its steps are added to a queue (including the Device ID). The cloud then begins working its way through the workflow, one step at a time. Each step executes to completion before the could will being processing of the following step, so each workflow is processed synchronously.

In a situation where multiple workflows are initiated for a single device, the cloud will process each workflow synchronously. However it is possible that the steps of different workflows will be interleaved. If one workflow is held up because a step has not finished, another workflow might complete several steps in the meantime.

Note: For this reason, it is important that workflows not have dependencies on the completion (or partial completion) of other workflows.

3.3.2 - Triggers

Learn about Triggers.

Triggers

Manual triggers

Manual triggers are used to create a shortcut for a set of steps that are done often. Instead of performing a number of individual tasks, in sequence, again and again, the user simple initiates a workflow each time. Consider this example:

Before:
    user steps:
        remote log into system
            Username:   user
            Password:   password
        scp /etc/log/file1.log cloud:/device[123]/files
        scp /etc/log/file2.log cloud:/device[123]/files
        scp /etc/log/file3.log cloud:/device[123]/files
        scp /etc/log/file4.log cloud:/device[123]/files
        scp /etc/log/file5.log cloud:/device[123]/files
        Log out
After:
    workflow definition:
        trigger:    manual
        name:       pull-files
        model:      zafaroni 2500
        steps:
            Run Command [zip /tmp/logfiles.zip /etc/log/file*.log]
            Run Command [curl -X POST -X POST "http://localhost:5465/mailink/v1/telemetry"
                -H "accept: */*" -H "Content-Type: application/json"
                -d '{"path":"/tmp/logfiles.zip","destinationPath":"logfiles.zip"}']

    user steps:
        initiate pull-files

Manual initiation of a workflow is done through the maiLink portal with no typing. This is obviously more efficient, but also means fewer errors are made.

Automated triggers

Automated triggers occur when the maiLink cloud detects that certain conditions have been met. Currently, automated triggers are all based on telemetry. We expect that to change in the coming months.

Telemetry triggers

Each time a telemetry message is received in the cloud, it is evaluated against all the workflows that are defined for that model (the model of the device that sent the telemtry message) with the same type as the message. As an example, if there are four workflows of type Event defined for model “Sanguis 2500”, and an Event telemetry message is received by the cloud froma Sanguis 2500 system, then the conditions in those four workflows will each be evaluated. It is possible that four workflows will be triggered.

Suppose that four workflows are defined as shown below. If a telemetry metric message is received from a Baristamatic 2500 system with “motor_rpm” value of 55, then you can see that three workflows are triggered (Workflow 4 has the right model, but does not meet the condition; Workflow 5 has the wrong model, so the condition is ignored).

Workflow Model Condition Triggered?
1 Baristamatic 2500 metric.name == “motor_rpm” and metric.value < 100 Yes
2 Baristamatic 2500 metric.name == “motor_rpm” and metric.value < 80 Yes
3 Baristamatic 2500 metric.name == “motor_rpm” and metric.value < 60 Yes
4 Baristamatic 2500 metric.name == “motor_rpm” and metric.value < 40 No
5 Baristamatic E130 metric.name == “motor_rpm” and metric.value < 60 No

For details on the syntax for conditions, please see Condition Syntax.

3.3.3 - Condition Syntax

Learn about Workflow Trigger Condition Syntax.

Telemetry trigger conditions are written like logical statements that are to be evaluated, but without the common “if/then” syntax. The conditions can be complex, including and/or logic, and nested parenthesis.

There is a lot of information below because you can do some quite sophisticated things in triggers. To help you find your way, here’s a handy index (with links):

Class Subclass A Few Examples
Operands Literal Booleans, integers, floats
Telemetry event.code, metric.value, status.name
Devices device.id, device.tags["tag_name"]
Operators Arithmetic + - * /
Comparison == != < > <= >=
Logical and, or, not
String contains, startswith, endswith

To help guide you, there are some condition examples here.

The syntax can be quite complicated and, in fact, there are very additional built-in commands not described in this page. maiLink leverages a 3rd party libary for expression resolution in condition syntax evaluation. The details shown above represent the core features of the condition syntax. There are, however, further ways to construct conditions. maiData has neither used nor tested the additional methods that you can find described at antonmedv’s GitHub page at Expr Library Documentation.

Operands

Conditions are logical statements comprised of operands and operators. The operands allow you to test telemetry messages that are received. They also allow testing of other information about the device.

Literal Operands

Type Description Example
Boolean One of: true, false false
Integer An integer value. 55.3
Float A floating point value. 55.3
String ASCII characters enclosed in single or double quotes (' ' or " “). “motor_rpm”
Array A list of literal operand values enclosed in square brackets. [1, 2, 3]
Range An inclusive range of integers 4..45

Operands From Telemetry Messages

Operand Type Description
event.code string The code of the event.
event.text string A text blurb that describes the the event.
event.severity number The severity level of the event.
metric.label string The identifier of the metric.
metric.value number The value of the metric.
status.name string The identifier of the status.
status.value string The value of the status.

Operands From Device Information

Operand Type Description
device.id string The id of the device. This may be useful with the “contains”, “startswith” or “endswith” string operators.
device.name string The name of the device. This may be useful with the “contains”, “startswith” or “endswith” string operators.
device.status["status_name"] string The most recently received status value of status telemetry “status_name”.
device.tags["tag_name"] string The current value of device tag “tag_name”.
device.type string The device model (also known as device type) is defined for the workflow, so is probably not needed as an operand.

Operators

Arithmetic Operators

maiLink workflows support common arithmetic operators in condition statements:

Operator Description Example
+ Addition metric.value * 3
- Subtraction metric.value - 3
* Multiplication metric.value * 3
/ Division metric.value / 3
% Modulus metric.value % 3
^ or ** Exponent metric.value ^ 3

Comparison Operators

maiLink workflows support common comparison operators in condition statements:

Operator Description Example
== Tests if the values of two operands are equal or not; if yes, the condition becomes true. event.code == “E1003”
!= Tests if the values of two operands are equal or not; if the values are not equal, then the condition becomes true. status.label != “sw_version”
< Tests if the value of left operand is less than the value of the right operand; if yes, the condition becomes true. Intended for metric messages because it is a numeric test. metric.value < 55
> Tests if the value of left operand is greater than the value of right operand; if yes, the condition becomes true. Intended for metric messages because it is a numeric test. metric.value > 55
<= Tests if the value of left operand is less than or equal to the value of right operand; if yes, the condition becomes true. Intended for metric messages because it is a numeric test. metric.value <= 55
>= Tests if the value of the left operand is greater than or equal to the value of the right operand; if yes, the condition becomes true. Intended for metric messages because it is a numeric test. metric.value >= 55
in Tests if the first operand is an integer within the range of values specified by second operand. metric.value in 33..166

Logical Operators

maiLink workflows support common comparison operators in condition statements:

Operator Description Example
not or ! Logical NOT operator. not (event.code == “E1003”)
and or && Logical AND operator. (event.code == “E1003”) and (event.severity > 50)
or or || Logical OR operator. (event.code == “E1003”) and (event.severity > 50)
not Logical NOT operator. not (event.code == “E1003”)

String Operators

maiLink workflows support the common string operators in condition statements:

Operator Description Example
contains Tests if first operand contains the second operand in its entirety, with identical case. event.code contains “E10”
endswith Tests if end of the second operand is the exact ending of the second operand, with identical case. metric.label endswith “_rpm”
in Tests if the first operand equals an element in an array of strings, with identical case. status.name in [“SWversion”,“SWoriginal”]
matches Tests if the value of first operand is matches the regular expression given as the second operand event.code matches “^.*_rpm_.*”
startswith Tests if end of the second operand is the exact beginning of the first operand , with identical case. metric.label startswith “airlock_”

Condition Syntax Examples

These examples show some more complicated types of condition syntaxes.

Type Example Condition
Event event.code == “E1003”
Event event.code == “E0113” and device.tags[“contract”] in [“Warranty”,“Expired”]
Event event.code == “E1003” or event.code == ”E1388”
Event event.code in [“E1003”,”E1388”]
Event event.code matches “^E1[0-9][0-9][0-9]$”
Metric metric.label == “motor_rpm” and metric.value < 55
Metric metric.label == “motor_rpm” and metric.value < 55 and metric.value > 33
Metric metric.label == “motor_rpm” and metric.value < 55 and device.tags[“motor size”] == ”small”
Status status.name == “version” and status.value == “1.3.2”
Status Status.name == ”version” and status.value != device.tags[“RequiredVersion”]

3.3.4 - Steps

Learn about Workflow Steps.

Think of workflow steps as individual units of work. Together, performed in the right sequence, the workflow steps complete a body of work. This is no different that steps done manually, but without the possibility of human error such as leaving out a step, or performing a step incorrectly. When you create a new workflow you define the type, the model, the condition (when required) and give it a name. Then you define the individual steps that will be performed when the workflow is initiated. maiLink provide a drag-and-drop user interface for defining those steps, called the Step Builder.

Step Builder

When you create a new workflow you are first presented with an empty Step Builder screen. It shows only the Trigger and End boxes in the workflow diagram, and the initial focus is on defining the Trigger.

Trigger Empty

By default the trigger is set to an Event type, but you can select Manual (User_Dispatch), Metric or Status with the pull-down. For Event, Metric or Status type triggers, you then define the Condition. Default strings are inserted to give you some guidance and help you remember what to do.

  1. Type the condition
    • As you type, syntactic suggestions are made.
    • Select from the suggestions with the arrow keys.
    • Add a suggestion to your condition with the tab key.
  2. Click Save to store your condition
  3. The Trigger box will be updated.

Trigger Updated

Then specify the workflow details at the left:

  1. Type in a name for the workflow.
  2. Select the Model that will use this workflow.

Workflow Details

Note: Today you can select multiple models – this is a design issue that will be removed in a future release.
Note: Please select only a single model for each workflow. Why? Because there is only one copy of each workflow in the cloud. If you assign one workflow to multiple models, and in the future want to modify the workflow for just one of the models, you will have no way to easily do that.
Note: In a future version we will provide a way to create a copy of a workflow and reassign that copy to a different model. Then they can easily diverge.

Individual Steps

Within the workflow you can define the series of steps that will be executed to accomplish the goal. Order is important as the steps will be run in sequence, and no step will begin until the prior step and completed. Any step that fails will terminate the workflow.

Step Description Parameters Processing Location Release
Run Command Runs a single command line on the target device. Command Device Mar 2023
Set Device Tag Sets the value of a Device Tag. Device Tag Name Cloud ~Mar 2023
Download File Runs a single command line on the target device. Command Device ~Apr 2023
Set Alarm Sets an alarm. Alarm Name Cloud ~Apr 2023
Clear Alarm Clears an alarm. Alarm Name Cloud ~Apr 2023

Defining the Workflow Steps.

To use the Step Builder:

  1. Drag (left mouse click and hold) a color step block from the Add Steps are at the left.
    • As you drag one, you will see circled green plus signs appear indicating where you need to drag the block.
    • Release the mouse click when the tip of the cursor is over the desired circle.
      Workflow Step Drag
  2. If the step requires inputs, they will open by default at the left.
    • Enter the required data for the step.
    • Click Save to store the parameters for that step.
      Workflow Step Data
  3. Continue dragging and dropping step blocks onto the workflow diagram until all the steps are in place.
  4. When all the steps are in place, click Save (bottom left) to save the workflow.

3.4 - Device Health Indicators

Descriptions of the visual device status indicators and what they mean.

Agent Status

The maiLink Agent will automatically send regular health updates to the cloud. These will be displayed in the maiLink UI as a circular icon. The icon meanings are described in the following table:

Icon Meaning
fiber_manual_record The Agent has sent a health update in the last 3 minutes
fiber_manual_record The Agent has not sent a health update in the last 3 minutes
fiber_manual_record The Agent has sent a health update in the last hour
fiber_manual_record The Agent has never sent a health update

App Status

Applications can provide heartbeat notifications via the Telemetry API. These will be displayed in the maiLink UI as a heart-shaped icon. The icon meanings are described in the following table:

Icon Meaning
favorite Your application has reported a heartbeat in the last 3 minutes
heart_broken Your application has not reported a heartbeat in the last 3 minutes
heart_broken Your application has not reported a heartbeat in the last hour
favorite_border Your application has never reported a heartbeat telemetry event

3.5 - maiLink Glossary

Common terms and language used in the maiLink application.

Basic Terms

These terms are best understood if read through in sequence.

Term Description
maiLink The shorthand name for the maiLink SRM platform, the maiLink Apps that run on the maiLink SRM platform, and all of its features and capabilities.
Device A manufactured product that is installed in the field.
Device Type A class of manufactured products, such a those of the same make and model and, perhaps, with similar options.
Partner A manufacturer or other business that is a direct user of maiLink SRM softare (E.g. they use it to track and monitor their products).
Customer An entity that purchases and uses products manufactured by a maiLink Partner.
Term Description
maiLink SRM The name of the maiData Service Relationship Management (SRM) software platform for tracking fleets of deployed devices and secure communications with those devices.
maiLink Apps The collection of optional maiLink Apps are available on the maiLink SRM platform and can be enabled / disabled for each Device Type:
App Description
Manage This App allows Device Types to be created for each brand and model of product, and Devices to be created for each serial number.
Health This App sends an “Agent Alive” health message from the Agent to the Cloud at a regular interval.
Access This App provides secure remote access from an installed Client to deployed devices in the field.
Telemetry This App sends asynchronous messages from the Agent to the Cloud.
Command This App automates service tasks. Each Command is a script that combines individual file transfers to/from the device as well as remotely executed commands that are run on the device. Command allows scripts to be broadcast to one Device, a selected group of Deviecs (all of the same Device Type), or all the Devices of a Device Type.
Type Description
Metrics Numeric values that change over time.
Events Momentary occurences on the Devices, such as “Door opened”, “System restarted”. Each Event has a Severity Level, a Code that identifies the type of Event, and a Descriptor that is human-readable.
Status Describes the the current state of something in the Devices, such a “Door is Open” or “X-ray Tube is on”. Each Status has a Code that identifies the component being described, and a Descriptor that is human-readable.
Type Description
Agent The maiLink software module built into products that can connect to the maiLink SRM software platform.
Client The maiLink software module installed on Partner service technicians if/when they need to remotely access deployed Devices.
Cloud The maiLink software cloud-based database, management, portal and secure communications software.
Router A maiLink software accessory with built-in Agent that, when installed in a Customer facility and with the agreement of the Customer, allows the Partner to gain Access to non-maiLink compatible, network-connected systems.

Types of Protected Information

These terms are usually used in the context of Information Security (SOC1, SOC2, SOC3, ISO-27001), Patient Privacy (HIPAA) and/or Data Protection (GDPR).

Type Description
PCI Payment Card Information.
PHI Payment Card Information.
PII Payment Card Information.

Other Terms

Type Description
PDLC maiLink uses a Product Development Life Cycle process for developing maiLink products.
VA In computing, a Virtual Appliance (VA) is considere a software equivalent of a hardware device, usually in the form of a preconfigured software solution.
VM In computing, a Virtual Machine (VM) is the virtualization / emulation of a computer system.

4 - FAQs

Frequently Asked Questions about maiLink SRM software security.

4.1 - Security FAQs

Frequently Asked Questions about maiLink SRM software security.

4.1.1 - maiLink Agent

Security information for the maiLink Agent software module.

General

  1. How is the Agent integrated with the customer infrastructure? The Agent is an independent software module that runs within the product, which is installed and network-connected within the customer’s facility.

  2. In what year was the Agent first offered? 2022

  3. Is the Agent provided as Software-as-a-Service? Yes, via Partner’s maiLink SRM subscription.

  4. Is the Agent to be installed in the cloud (not within the customer location)? No, although it works closely with maiLink SRM, which is cloud-based.

  5. Is the Agent to be locally installed (within the customer location)? Yes, but only as a software component within the product.

  6. What environment does the Agent run in? The Agent is pre-installed into the product by the Partner.

  7. What is the purpose of the Agent? The Agent is designed to allow Partners to gain Access to the systems and software that they are obligated and authorized to service within Customer facilities.

  8. Who is the best maiData contact for questions or concerns about the Agent security? Adam Zenner, CTO and CISO.

  9. Why would a Customer permit installation of a product containing the Agent? To allow the Partner to service the product or provide the desired services in the Customer facility.

Access

  1. Do all Partner employees access the Agent via a single login portal? Yes, via maiLink SRM.

  2. Does the Agent maintain an account lock-out feature, activated after a number of failed login attempts? Not applicable.

  3. Does the Agent prohibit re-use of prior passwords? Not applicable.

  4. Is each Partner's implementation accessed via a unique login portal? Yes, via maiLink SRM.

  5. What is the URL for the the Agent? Each the Agent does not have a URL.

Audit Logs

  1. Are the Agent audit records time-stamped? No. The audit logs in maiLink SRM provide that function.

  2. Are all the Agent non-local maintenance and diagnostic activities approved and monitored? Yes.

  3. Can the Agent be configured to select which auditable events are captured in the audit log? No. The audit logs in maiLink SRM provide that function.

  4. Do the Agent audit logs contain enough information to establish the identify of the user/subject associated with the event? Not applicable. The audit logs in maiLink SRM provide that function.

  5. Do the Agent audit logs contain enough information to establish the source of the event? Not applicable. The audit logs in maiLink SRM provide that function.

  6. Do the Agent audit logs contain enough information to establish what type of event occurred? Not applicable. The audit logs in maiLink SRM provide that function.

  7. Do the Agent audit logs contain enough information to establish when the event occurred? No. The audit logs in maiLink SRM provide that function.

  8. Do the Agent audit logs contain enough information to establish where the event occurred? No. The audit logs in maiLink SRM provide that function.

  9. Does the Agent allow generation of custom audit reports? Yes, through maiLink SRM.

  10. Does the Agent generate an alert in the event of an audit processing failure? No.

  11. Does the Agent keep audit logs? Yes, via maiLink SRM.

  12. Does the Agent protect audit records from unauthorized access, modification and deletion? Yes, via maiLink SRM.

  13. Does the Agent record Failed Log In events in its audit logs? Not applicable.

  14. Does the Agent record Files / Records Deleted events in its audit logs? Not applicable.

  15. Does the Agent record Files / Records Modified events in its audit logs? Not applicable.

  16. Does the Agent record Files / Records Viewed events in its audit logs? Not applicable.

  17. Does the Agent record Log In events in its audit logs? Yes, via maiLink SRM.

  18. Does the Agent record Log Out events in its audit logs? Yes, via maiLink SRM.

  19. Does generation of an the Agent audit report alter the original content or time stamp of the audit record? Not applicable.

  20. Does maiData maintain records for the Agent non-local maintenance and diagnostic sessions? Yes.

  21. Is the information captured in the Agent audit logs sufficient for system and user performance investigations? Yes, via maiLink SRM.

Authentication

  1. Does the Agent protect the authenticity of communication sessions? Yes.

  2. Does the Agent support multi-factor authentication? Not applicable.

  3. Does the Agent system obscure the authenticator/password during the authentication process? Not applicable.

  4. Does the Agent uniquely identify and authenticate devices before establishing communication with the Cloud? Yes, using unique encrypted JSON Web Tokens (JWTs).

  5. Does the Agent uniquely identify and authenticate devices before establishing communication within the Customer facility? Not applicable.

  6. Does the Agent use managed LDAP services for identification and authentication? No.

  7. How does the Agent achieve MFA? Not applicable.

  8. Is the Agent authenticator content protected from unauthorized disclosure and modification? Not applicable.

  9. Is token-based authentication used? Not applicable.

Configuration

  1. How do you do a factory reset on the Agent? Remove and reinstall the software.

  2. How is IP Address of the the Agent configured? The the Agent is a software module within the product. Partner can provide information about configuring product IP address.

  3. How is the configuration of the Agent controlled? The Agent configuration is controlled in Cloud by Partner.

Credentials

  1. Can an the Agent user request a password reset? Not applicable.

  2. Can any user with the Agent credentials add, remove or modify Administrator users? Yes, but that capability will be removed in early 2022.

  3. Can Customer IT personnel be given credentials to the Agent? Yes, if the Partner agrees to provide credentials to the Customer.

  4. Can the Customer configure the default requirements for passwords? Not applicable.

  5. Does the Agent allow federation of credentials? Yes, via maiLink SRM.

  6. Does the Agent require passwords to be at least 8 characters long? Not applicable.

  7. Does the Agent require passwords to contain at least one non-alphanumeric character? Not applicable.

  8. Does the Agent require passwords to contain at least one numeric digit? Not applicable.

  9. Does the Agent require passwords to contain mixed-case alpha characters? Not applicable.

  10. Does the Agent require passwords to expire every 90 days? Not applicable.

  11. Does the Agent support federated identity? No. That function is provided in maiLink SRM.

  12. Must an the Agent user establish their own password at first login? Not applicable.

  13. What are the minimum requirements for an the Agent password in terms of length and complexity? Not applicable.

  14. What credentials does a user need to access the Agent locally (from inside the Customer firewall)? Not supported. All the Agent access is done remotely.

  15. What credentials does a user need to access the Agent remotely (from outside the Customer firewall)? Partner-authorized maiLink SRM credentials and role permissions.

Data Access

  1. Will the Agent be used to transmit Customer Employee Information? Only if necessary, authorized and permitted by Customer.

  2. Will the Agent be used to transmit PCI? Only if necessary, authorized and permitted by Customer.

  3. Will the Agent be used to transmit PHI? Only if necessary, authorized and permitted by Customer.

  4. Will the Agent be used to transmit PII? Only if necessary, authorized and permitted by Customer.

  5. Will the Partner be able to access Customer Internal / Proprietary Information via the Agent? Only if authorized and permitted by Customer.

  6. Will the Partner be able to access Employee Information via the Agent? Only if authorized and permitted by Customer.

  7. Will the Partner be able to access PCI via the Agent? Only if authorized and permitted by Customer.

  8. Will the Partner be able to access PHI via the Agent? Only if authorized and permitted by Customer.

  9. Will the Partner be able to access PII via the Agent? Only if authorized and permitted by Customer.

  10. Will the Partner be able to receive transmitted data from Customer, via the Agent? Yes, as authorized and permitted by Customer.

  11. Will the Partner have access to a database or application, via the Agent, that stores or transmits Customer data? Yes, as authorized and permitted by Customer.

  12. Will the Partner have access to infrastructure, via the Agent, that stores or transmits Customer data? Yes, as authorized and permitted by Customer.

  13. Will the Partner have access to the Customer network, via the Agent, for on-site support? No.

  14. Will the Partner have access to the Customer network, via the Agent, for remote support? Yes, as authorized and permitted by Customer.

  15. Will the Partner use Customer computer systems to access and/or transmit Customer data via the Agent? No.

  16. Will the Partner use Partner computer systems to access and/or transmit Customer data via the Agent? Yes. But what can be accessed is based on access authorized and permitted by Customer.

Documentation

  1. Does maiData administrator documentation for the Agent include configuration, installation and operation information? Not applicable.

  2. Does maiData administrator documentation for the Agent include known vulnerabilities regarding configuration and use of administrator functions? Not applicable.

  3. Does maiData administrator documentation for the Agent include security functions and mechanisms information? Not applicable.

  4. Does maiData include requirements, descriptions and criteria in the acquisition contract for the Agent? Yes, but only an abbreviated description. The remaining requirements, descriptions and criteria are in publicly available documents.

  5. Does maiData maintain administrator documentation for the Agent? Not applicable.

  6. Does maiData maintain any documentation which includes the details of the Agent's security configuration specifications? Yes. Available on request.

  7. Does maiData maintain current accurate documentation of the components in the the Agent? Yes.

  8. Does maiData maintain user documentation for the Agent? Not applicable.

  9. Does maiData user documentation for the Agent include information on methods for user interaction which make the Agent use more secure? Not applicable.

  10. Does maiData user documentation for the Agent include information on user responsibility in maintaining the Agent security? Not applicable.

  11. Does maiData user documentation for the Agent include information on user-accessible security functions and how to use them? Not applicable.

  12. Is any non-local maintenance and diagnostic activity performed on the the Agent (E.g. via network)? Yes, via maiLink SRM.

  13. Is there documentation outlining who, when and how the Agent can be configured? Not applicable.

Partner Responsibilities

  1. Are all approved the Agent configuration changes implemented in a timely manner? Not applicable.

  2. Does maiData authorize a list of authorized maintenance personnel? No. The Partner authorizes maintenance personnel.

  3. Does maiData define the timing of the Agent patches? No. The Partner is responsible for applying such patches.

  4. Does maiData ensure that personnel performing maintenance on the Agent have the required access authorizations? No. The Partner authorizes maintenance personnel and provides them with access authorization.

  5. Does maiData enter access agreements with employees that have access to the Agent? No. It is the responsibility of the Partner to have access agreements with their employees if they are to be authorized to access the the Agent.

  6. Does maiData have personnel sanctions policies and procedures? No. It is the responsibility of the Partner to establish third-party access control procedures for external parties who are granted access the the Agent.

  7. Does maiData have termination procedures in place for those with access to the Agent? No. It is the responsibility of the Partner to handle the termination of any of their employees that is authorized to access the the Agent.

  8. Does maiData have third-party access control procedures for external parties granted access to the Agent? No. It is the responsibility of the Partner to establish third-party access control procedures for external parties who are granted access the the Agent.

  9. Does maiData have transfer procedures in place for those with access to the Agent? No. It is the responsibility of the Partner to handle transfer between employees of authorization to access the the Agent.

  10. Does maiData maintain a list of authorized maintenance personnel for the Agent? No. The Partner authorizes maintenance personnel.

  11. Does maiData periodically review access agreements for employees that have access to the Agent? No. It is the responsibility of the Partner to periodically review access agreements with their employees if they are authorized to access the the Agent.

  12. Does maiData provide remote support / maintenance services that would involve maiData employees accessing the Agent? No, by policy. The Partner may specifically request direct support, and authorize access to the the Agent by maiData personnel, however this would not be a common occurrence.

  13. Does maiData screen individuals prior to authorizing access to the Agent? No. It is the responsibility of the Partner to determine which of their employees is authorized to access the the Agent.

  14. How will the Agent access, transmit or store Customer's data? Partner may access, transmit or store Customer's data using the Agent as a tool. the Agent does not independently access, transmit or store Customer data.

  15. Who can create credentials for the Agent? Not applicable.

SDLC Procedures

  1. Are the Agent flaws identified, reported and corrected? Yes.

  2. Are the Agent software and firmware updates tested for effectiveness and potential side effects before incorporation? Yes. Because the the Agent installs in the Partner’s product, the Partner is responsible for this.

  3. Are all the Agent configuration changes documented? Not applicable.

  4. Are configurable changes to the Agent documented? Not applicable.

  5. Do the documented the Agent configuration settings reflect the most restrictive mode consistent with operational requirements? Not applicable.

  6. Does maiData analyze changes to the Agent to determine potential security impacts prior to change implementation. Yes.

  7. Does maiData apply information system security engineering principles in the Product Development Life Cycle of the Agent? Yes.

  8. Does maiData approve, control and monitor the Agent maintenance tools? Yes.

  9. Does maiData automatically apply software patches to the Agent? Yes, using an auto-update mechanism.

  10. Does maiData categorize the Agent patches based on severity? Yes, maiData classifies patches as minor, major, and critical.

  11. Does maiData check for potential adverse impact on security controls following maintenance or repair actions? Not applicable.

  12. Does maiData define a comprehensive life cycle for the Agent? No, but it is planned as part of our ISO 27001 process.

  13. **Does maiData develop, document and implement a configuration management plan for the Agent that addresses roles, responsibilities and configuration No, but it is planned as part of our ISO 27001 process.

  14. Does maiData document the Agent configuration changes that deviate from the established settings? Not applicable.

  15. Does maiData have a process for identifying configuration items during the SDLC? Yes.

  16. Does maiData maintain a formal security patch management process for the Agent? No, not at this time.

  17. Does maiData maintain documented policies and procedures for maintenance of the Agent? Not applicable.

  18. Does maiData perform vulnerability testing as part of the Agent's Software Development LifeCycle (SDLC)? Yes, using Zap software to test against the Open Web Application Security Project (OWASP) requirements.

  19. Does maiData protect the configuration management plan from unauthorized disclosures and modifications? Not applicable.

  20. Does maiData require the Agent developers to conform to maiData-approved configuration changes? Yes.

  21. Does maiData require the Agent developers to create and implement a security assessment plan for the Agent? No, but maiData is in the process of developing such policies and procedures for conformance with ISO 27001:2013.

  22. Does maiData require the the Agent developers security assessment plan to produce evidence of the execution of the security assessment plan? No, but maiData is in the process of developing such policies and procedures for conformance with ISO 27001:2013.

  23. How often does maiData perform penetration tests on the Agent? Once per software release.

  24. Is there active monitoring of the Agent configuration changes? Not applicable.

  25. Is there documentation outlining the baseline configuration of the Agent? Not applicable.

  26. What environments does maiData use in development of patches for the Agent? maiData uses Development, QA (including Test) and Production environments to verify and validate patches.

Security

  1. Are strong authenticators/passwords used in the establishment of the Agent non-local maintenance and diagnostic sessions? Yes.

  2. Are there any known vulnerabilities within the Agent? No.

  3. Can the Agent credentials be federated with maiLink SRM? Yes.

  4. Does the Agent come with its own antivirus solution? No. It is reliant on host computer’s antivirus solution.

  5. Does the Agent come with its own malware protection? No. It is reliant on host computer’s malware protection.

  6. Does the Agent display the last user logon date and time to the user? No.

  7. Does the Agent encrypt data at rest? Yes, if the function is provided by the host computer.

  8. Does the Agent encrypt data in transit? Yes, for data transmitted between the Agent and Cloud.

  9. Does the Agent have a session lock after a period of inactivity that requires reauthentication? Not applicable.

  10. Does the Agent include any collaborative devices (cameras, microphones, etc)? No.

  11. Does the Agent limit the number of concurrent sessions for the user? Not applicable.

  12. Does the Agent prevent user actions that can be performed on the system without identification and authentication? Yes.

  13. Does the Agent provide system use notification that includes privacy and security notices before granting access? No.

  14. Does the Agent separate user functionality from administrative functionality? Not applicable.

  15. Does the Agent store passwords in an encrypted format? Not applicable.

  16. Does the Agent terminate the session after predefined circumstances? Yes, via maiLink SRM.

  17. Does the Agent use cryptographic mechanisms to recognize changes to information (such as hashing)? No.

  18. Does the Agent use cryptographic protocols to protect transmitted information? Yes.

  19. Does the Agent use managed LDAP services for identification and authentication? Not applicable.

  20. Does the Agent use mechanisms for authentication to a cryptographic module? No.

  21. Does maiData have any automated or manual monitoring of the Agent configuration changes? Not applicable.

  22. Does maiData restrict or prohibit the use of any the Agent functions, ports, protocols and/or service that are not essential? Yes.

  23. Does maiData retain records of the Agent configuration changes? Not applicable.

  24. Does maiData review proposed the Agent configuration changes using defined security impact analyses? Not applicable.

  25. Has the Agent undergone any major platform changes, upgrades or enhancements in the past six months? No.

  26. How does the Agent keep Customers secure? The Agent establishes secure communications to Cloud using a small number of outbound ports, encryption and short-term certificates.

  27. Is user installation of the Agent restricted and monitored? Yes. Use of the the Agent is restricted and must be authorized by the Partner.

  28. What cryptographic protocols does the Agent use to protect transmitted information, including strength? AES 128-bit.

  29. What encryption method does the Agent use to encrypt data at rest? Depends on the host computer.

  30. What encryption method does the Agent use to encrypt data in transit? AES 128-bit

  31. What is the inactivity period before the Agent terminates a session ? Not applicable.

  32. What was the date of the most recent the Agent vulnerability test? 2021-01-29.

  33. Will the Agent be used to transmit Customer Employee Information? Only if used by the Partner for that purpose deliberately or inadvertently.

  34. Will the Agent be used to transmit PCI? Only if used by the Partner for that purpose deliberately or inadvertently.

  35. Will the Agent be used to transmit PHI? Only if used by the Partner for that purpose deliberately or inadvertently.

  36. Will the Agent be used to transmit PII? Only if used by the Partner for that purpose deliberately or inadvertently.

  37. Are the Agent non-local maintenance and diagnostic sessions terminated after completion? Yes.

  38. Does maiData implement the Agent patches categorized as critical within 72 hours of patch release? Yes, depending on the requirement to notify end-user customers of changes associated with a specific patch.

  39. Does maiData maintain a disaster recovery policy which applies to the Agent? Yes. maiData security policies apply to all maiData products.

4.1.2 - maiData Corporation

Security information for maiData Corporation.

General

  1. Business name? maiData Corporation.

  2. Correspondence address? PO Box 50989, Palo Alto, CA 94303-0989, USA.

  3. Executive or Officer responsible for incident response? Adam Zenner, CTO and CISO.

  4. Executive or Officer responsible for information security? Adam Zenner, CTO and CISO.

  5. Has maiData ever experienced a data breach? No.

  6. Level of cyber liability insurance? $3M / $1M per incident.

  7. Location of maiData employees? maiData has a US-based virtual team.

  8. Number of employees? 6 full-time, 8 contract.

Compliance

  1. Does maiData maintain FedRAMP? Not directly, but our provider AWS does.

  2. Does maiData maintain HIPAA? Not directly, but our provider AWS does.

  3. Does maiData maintain HITRUST? Not directly, but our provider AWS does.

  4. Does maiData maintain ISO 27001:2013? Not yet, but our provider AWS does. maiData follows ISO 27001:2013 guidance and is in the process of adhering to this standard.

  5. Does maiData maintain NIST? Not directly, but our provider AWS does.

  6. Does maiData maintain PCI Compliance? Not directly, but our provider AWS does.

  7. Does maiData maintain SOC 2 Type 2? Not directly, but our provider AWS does.

  8. Does maiData maintain SOC 3? Not directly, but our provider AWS does.

  9. Does maiData maintain SSAE 16 / SOC 1 Type 1? Not directly, but our provider AWS does.

  10. Does maiData maintain SSAE 16 / SOC 1 Type 2? Not directly, but our provider AWS does.

Data Privacy

  1. Do maiData team members have access to Customer PHI or PII data? No, unless viewed in the course of providing technical support to a Partner, who controls that access. A partner may request that maiData enters into BA agreement with that Partners whose deployed systems may contain PHI data.

  2. Do maiData team members have access to Partner data? Yes, but limited. maiData has access to Partner billing information and contact information necessary for providing maiLink products and services. maiData has no access to other Partner data unless specifically requested to provide technical support to the Partner, who controls that access.

  3. Does maiData maintain policies and procedures for access control? No, but maiData is in the process of developing such policies and procedures for conformance with ISO 27001:2013.

  4. Does maiData share Customer data with any third party? No.

  5. Does maiData share Partner data with any third party? With the permission of the Partner, maiData may publish the fact that Partner uses maiLink software and quotes about the Partner’s experience in using maiLink software.

Incident Management

  1. Does maiData have a comprehensive Incident Response Plan? Yes, it is being developed as part of our ISO 27001 process.

  2. Does maiData have an incident handling process? Yes, it is being developed as part of our ISO 27001 process.

  3. Does maiData report security incidences to the appropriate personnel / government authorities in a timely manner? Yes.

  4. Does maiData respond to information spillage in a timely manner? Yes. Any spillage is treated as an incident.

  5. Does maiData track and document security incidents? Yes.

  6. Does maiData use incident response resources outside of the incident response team? No.

Quality Policy

  1. Does maiData document and monitor security training for its employees? Yes.

SDLC Procedures

  1. Are maiData information security functions outsourced? No.

Security Policy

  1. Does maiData adhere to information system security engineering principles throughout the Product Development Lifecycle for its products. Yes.

  2. Does maiData assign risk designations to all positions? Yes.

  3. Does maiData enter into BA agreements with Customers? Only if requested by the Customer.

  4. Does maiData enter into BA agreements with Partners? Yes.

  5. Does maiData establish screening criteria for individual filling positions of higher risk levels? Yes.

  6. Does maiData have BAAs in place? Yes, as necessary.

  7. Does maiData maintain a formal incident response policy? Yes.

  8. Does maiData maintain policies and procedures for System and Communications protection. No, but we will be developing these as we move towards ISO-27001:2013 conformance.

  9. Does maiData maintain policies and procedures for System and Information Integrity? No, but we will be developing these as we move towards ISO-27001:2013 conformance.

  10. Does maiData maintain policies and procedures for System and Services Acquisitions? No, but we will be developing these as we move towards ISO-27001:2013 conformance.

  11. Does maiData maintain SOC 2 Type 1? Not yet, but our provider AWS does. maiData follows SOC 2 Type 1 guidance and is in the process of adhering to this standard.

  12. Does maiData perform background checks on its employees? Yes, for full-time employees with access to maiData Information Systems.

  13. Does maiData perform incident response tests and analyze the results of those tests? No.

  14. Does maiData review and revise position risk designations periodically? Yes. Every three years.

  15. Does maiData support the capability to use cryptographic mechanisms to protect information at rest? Yes.

  16. Is flaw remediation incorporated into the maiData configuration management process? No, but we will be developing these as we move towards ISO-27001:2013 conformance.

  17. What cryptographic mechanisms and strengths does maiData employ to protect information at rest? AES 128-bit.

Training

  1. Are maiData team members HIPAA trained? Yes.

  2. Are maiData team members PHI trained? Yes.

  3. Are maiData team members PII trained? Yes.

  4. Are maiData team members provided with incident response training? Yes.

  5. Do maiData personnel receive role-based security training? Yes.

  6. Do maiData personnel undergo periodic security awareness training? Yes.

  7. Does maiData maintain policies and procedures for Personnel Security? Yes.

4.1.3 - maiLink Router

Security information for maiLink Router software.

General

  1. Has maiLink Router undergone any major platform changes, upgrades or enhancements in the past six months? No.

  2. How is maiLink Router integrated with the customer infrastructure? maiLink Router is an independent software device that is network-connected within the customer's facility. The customer IT department can use switch management to limit which IP addresses can be reached from the maiLink Router. The maiLink Router must have access to *.maidata.io on port 443 and 5000.

  3. In what year was maiLink Router first offered? 2022.

  4. Is maiLink Router provided as Software-as-a-Service? Yes, via Partner’s maiLink SRM subscription.

  5. Is maiLink Router to be installed in the cloud (not within the customer location)? No, although it works closely with maiLink SRM, which is cloud-based.

  6. Is maiLink Router to be locally installed (within the customer location)? Yes.

  7. What environment does maiLink Router run in? maiLink Router is provided as an Open Virtual Appliance (OVA) file that is installed in a virtual machine (VM) in the customer facility. It uses Linux as it’s operating system.

  8. What is the purpose of maiLink Router? maiLink Router is designed to allow Partners to gain Access to the systems and software that they are obligated and authorized to service within Customer facilities. From a single maiLink Router within the Customer facility, the Partner can access all network-connected products they need to support.

  9. Who is the best maiData contact for questions or concerns about maiLink Router security? Adam Zenner, CTO and CISO.

  10. Why would a Customer permit installation of maiLink Router? To allow the Partner to service the targeted product(s) or provide the desired service(s) in the Customer facility. maiLink Router allows the Partner to diagnose issues more quickly and provide some service without the time delay of having a field engineer visit the site.

Access

  1. Do all Partner employees access maiLink Router via a single login portal? Yes, via maiLink SRM

  2. Does maiLink Router maintain an account lock-out feature, activated after a number of failed login attempts? Yes, if credentials are federated

  3. Does maiLink Router prohibit re-use of prior passwords? Yes, if credentials are federated

  4. Is each Partner?s implementation accessed via a unique login portal? Yes, via maiLink SRM

  5. What is the URL for the maiLink Router? Each maiLink Router will have a unique URL that is created when the Partner requests access to the maiLink Router. The URL is different each time

Audit Logs

  1. Are maiLink Router audit records time-stamped? Yes.

  2. Are all maiLink Router non-local maintenance and diagnostic activities approved and monitored? Yes, via maiLink SRM.

  3. Can maiLink Router be configured to select which auditable events are captured in the audit log? No.

  4. Do maiLink Router audit logs contain enough information to establish the identify of the user/subject associated with the event? Yes.

  5. Do maiLink Router audit logs contain enough information to establish the source of the event? Yes, user and IP address.

  6. Do maiLink Router audit logs contain enough information to establish what type of event occurred? Yes, when combined with audit log information in maiLink SRM, to the level of log in / log out.

  7. Do maiLink Router audit logs contain enough information to establish when the event occurred? Yes, when combined with audit log information in maiLink SRM, to the level of log in / log out and the user.

  8. Do maiLink Router audit logs contain enough information to establish where the event occurred? Yes.

  9. Does maiLink Router allow generation of custom audit reports? Yes, through maiLink SRM.

  10. Does maiLink Router generate an alert in the event of an audit processing failure? No.

  11. Does maiLink Router keep audit logs? Yes, via maiLink SRM.

  12. “Does maiLink Router protect audit records from unauthorized access, modification and deletion?" Yes, via maiLink SRM.

  13. Does maiLink Router record Failed Log In events in its audit logs? No.

  14. Does maiLink Router record Files / Records Deleted events in its audit logs? maiLink Router has no visibility into file system changes in the target device.

  15. Does maiLink Router record Files / Records Modified events in its audit logs? maiLink Router has no visibility into file system changes in the target device.

  16. Does maiLink Router record Files / Records Viewed events in its audit logs? No.

  17. Does maiLink Router record Log In events in its audit logs? Yes, via maiLink SRM.

  18. Does maiLink Router record Log Out events in its audit logs? Yes, via maiLink SRM.

  19. Does generation of a maiLink Router audit report alter the original content or time stamp of the audit record? No.

  20. Does maiData maintain records for maiLink Router non-local maintenance and diagnostic sessions? Yes.

  21. Is the information captured in maiLink Router audit logs sufficient for system and user performance investigations? Yes, via maiLink SRM.

Authentication

  1. Does maiLink Router protect the authenticity of communication sessions? Yes.

  2. Does maiLink Router support multi-factor authentication? Yes, if configured.

  3. Does maiLink Router system obscure the authenticator/password during the authentication process? Yes.

  4. Does maiLink Router uniquely identify and authenticate devices before establishing communication with the Cloud? Yes, using unique encrypted JSON Web Tokens (JWTs).

  5. Does maiLink Router uniquely identify and authenticate devices before establishing communication within the Customer facility? Yes.

  6. Does maiLink Router use managed LDAP services for identification and authentication? Yes, if configured.

  7. How does maiLink Router achieve MFA? One-Time Password (OTP), if configured.

  8. Is maiLink Router authenticator content protected from unauthorized disclosure and modification? Yes through encryption.

  9. Is token-based authentication used? Yes.

Configuration

  1. How do you do a factory reset on maiLink Router? Remove and reinstall the software.

  2. How is IP Address of the maiLink Router configured? At first power-up, the maiLink Router uses DHCP to establish a network address. Customer IT personnel can then use the operating system to disable DHCP and set it to a fixed IP Address and subnet mask.

  3. How is the configuration of maiLink Router controlled? maiLink Router operating system configuration is controlled with on-system credential access, which can be configured for Single Sign-On or federation with other Customer credentialing. maiLink Router software version is controlled through a maiData Docker repository in the Cloud. Other maiLink Router configuration parameters are contolled locally, on the device.

Credentials

  1. Can a maiLink Router user request a password reset? No.

  2. “Can any user with maiLink Router credentials add, remove or modify Administrator users?" Yes, but that capability will be removed in early 2022.

  3. Can Customer IT personnel be given credentials to maiLink Router? Yes, if the Partner agrees to provide credentials to the Customer.

  4. Can the Customer configure the default requirements for passwords? Yes, if federated.

  5. Does maiLink Router allow federation of credentials? Yes, via maiLink SRM.

  6. Does maiLink Router require passwords to be at least 8 characters long? Yes, if credentials are federated.

  7. Does maiLink Router require passwords to contain at least one non-alphanumeric character? Yes, if credentials are federated.

  8. Does maiLink Router require passwords to contain at least one numeric digit? Yes, if credentials are federated.

  9. Does maiLink Router require passwords to contain mixed-case alpha characters? Yes, if credentials are federated.

  10. Does maiLink Router require passwords to expire every 90 days? Yes, if credentials are federated.

  11. Does maiLink Router support federated identity? No, but it is planned for a future release.

  12. Must a maiLink Router user establish their own password at first login? Yes.

  13. What are the minimum requirements for a maiLink Router password in terms of length and complexity? None at this time unless credentials are federated.

  14. What credentials does a user need to access maiLink Router locally (from inside the Customer firewall)? Customer IT personnel can access maiLink Router using credentials they create. Partner service personnel only access maiLink Router via maiLink SRM.

  15. What credentials does a user need to access maiLink Router remotely (from outside the Customer firewall)? Partner-authorized maiLink SRM credentials and role permissions.

Data Access

  1. Will maiLink Router be used to transmit Customer Employee Information? Only if necessary, authorized and permitted by Customer.

  2. Will maiLink Router be used to transmit PCI? Only if necessary, authorized and permitted by Customer.

  3. Will maiLink Router be used to transmit PHI? Only if necessary, authorized and permitted by Customer.

  4. Will maiLink Router be used to transmit PII? Only if necessary, authorized and permitted by Customer.

  5. Will the Partner be able to access Customer Internal / Proprietary Information via maiLink Router? Only if authorized and permitted by Customer.

  6. Will the Partner be able to access Employee Information via maiLink Router? Only if authorized and permitted by Customer.

  7. Will the Partner be able to access PCI via maiLink Router? Only if authorized and permitted by Customer.

  8. Will the Partner be able to access PHI via maiLink Router? Only if authorized and permitted by Customer.

  9. Will the Partner be able to access PII via maiLink Router? Only if authorized and permitted by Customer.

  10. Will the Partner be able to receive transmitted data from Customer, via maiLink Router? Only if authorized and permitted by Customer.

  11. Will the Partner have access to a database or application, via maiLink Router, that stores or transmits Customer data? Only if authorized and permitted by Customer.

  12. Will the Partner have access to infrastructure, via maiLink Router, that stores or transmits Customer data? Only if authorized and permitted by Customer.

  13. Will the Partner have access to the Customer network, via maiLink Router, for on-site support?" Yes, by remotely accessing the maiLink Router via maiLink SRM.

  14. Will the Partner have access to the Customer network, via maiLink Router, for remote support?" Yes, as authorized and permitted by Customer.

  15. Will the Partner use Customer computer systems to access and/or transmit Customer data via maiLink Router? No.

  16. Will the Partner use Partner computer systems to access and/or transmit Customer data via maiLink Router? Yes. But what can be accessed is based on access authorized and permitted by Customer.

Documentation

  1. Does maiData administrator documentation for maiLink Router include configuration, installation and operation information? Yes, as applicable.

  2. Does maiData administrator documentation for maiLink Router include known vulnerabilities regarding configuration and use of administrator functions? Yes, as applicable.

  3. Does maiData administrator documentation for maiLink Router include security functions and mechanisms information? Yes, as applicable.

  4. Does maiData include requirements, descriptions and criteria in the acquisition contract for maiLink Router? Yes, but only an abbreviated description. The remaining requirements, descriptions and criteria are in publicly available documents.

  5. Does maiData maintain administrator documentation for maiLink Router? Yes, as applicable.

  6. Does maiData maintain any documentation which includes the details of maiLink Router?s security configuration specifications? Yes. Available on request.

  7. Does maiData maintain current accurate documentation of the components in the maiLink Router? Yes.

  8. Does maiData maintain user documentation for maiLink Router? Yes.

  9. Does maiData user documentation for maiLink Router include information on methods for user interaction which make maiLink Router use more secure? Yes, as applicable.

  10. Does maiData user documentation for maiLink Router include information on user responsibility in maintaining maiLink Router security? Yes, as applicable.

  11. Does maiData user documentation for maiLink Router include information on user-accessible security functions and how to use them? Yes, as applicable.

  12. Is any non-local maintenance and diagnostic activity performed on the maiLink Router (E.g. via network)? Yes, via maiLink SRM.

  13. Is there documentation outlining who, when and how maiLink Router can be configured? Yes.

Partner Responsibilities

  1. Are all approved maiLink Router configuration changes implemented in a timely manner? N/A. The Partner controls the application of configuration changes.

  2. Does maiLink Router come with its own antivirus solution? No. However, maiData allows Partners to install antivirus solution on maiLink Router if desired.

  3. Does maiLink Router come with its own malware protection? No. However, maiData allows Partners to install malware protection on maiLink Router if desired.

  4. Does maiData authorize a list of authorized maintenance personnel? No. The Partner authorizes maintenance personnel.

  5. Does maiData document maiLink Router configuration changes that deviate from the established settings? No. The Partner controls configuration changes.

  6. Does maiData ensure that personnel performing maintenance on maiLink Router have the required access authorizations? No. The Partner authorizes maintenance personnel and provides them with access authorization.

  7. Does maiData enter access agreements with employees that have access to maiLink Router? No. It is the responsibility of the Partner to have access agreements with their employees if they are to be authorized to access the maiLink Router.

  8. Does maiData have personnel sanctions policies and procedures? No. It is the responsibility of the Partner to establish third-party access control procedures for external parties who are granted access the Agent.

  9. Does maiData have termination procedures in place for those with access to maiLink Router? No. It is the responsibility of the Partner to handle the termination of any of their employees that is authorized to access the maiLink Router.

  10. Does maiData have third-party access control procedures for external parties granted access to maiLink Router? No. It is the responsibility of the Partner to establish third-party access control procedures for external parties who are granted access the maiLink Router.

  11. Does maiData have transfer procedures in place for those with access to maiLink Router? No. It is the responsibility of the Partner to handle transfer between employees of authorization to access the maiLink Router.

  12. Does maiData maintain a list of authorized maintenance personnel for maiLink Router? No. The Partner authorizes maintenance personnel.

  13. Does maiData periodically review access agreements for employees that have access to maiLink Router? No. It is the responsibility of the Partner to periodically review access agreements with their employees if they are authorized to access the maiLink Router.

  14. Does maiData provide remote support / maintenance services that would involve maiData employees accessing maiLink Router? No, by policy. The Partner may specifically request direct support, and authorize access to the Agent by maiData personnel, however this would not be a common occurrence.

  15. Does maiData restrict or prohibit the use of any maiLink Router functions, ports, protocols and/or service that are not essential? No. The Partner controls configuration changes.

  16. Does maiData retain records of maiLink Router configuration changes? No. The Partner controls configuration changes.

  17. Does maiData review proposed maiLink Router configuration changes using defined security impact analyses? No. Configuration changes are made by the Partner.

  18. Does maiData screen individuals prior to authorizing access to maiLink Router? No. It is the responsibility of the Partner to determine which of their employees is authorized to access the maiLink Router.

  19. “How will maiLink Router access, transmit or store Customer?s data?" Please refer to Partner policies and procedures. maiLink Router does not independently access, transmit or store Customer data.

  20. Is there active monitoring of maiLink Router configuration changes? No. The Partner controls configuration changes.

  21. Who can create credentials for maiLink Router? Partner maiLink SRM Administrator.

  22. Will maiLink Router be used to transmit Customer Employee Information? Please refer to Partner policies and procedures.

  23. Will maiLink Router be used to transmit PCI? Please refer to Partner policies and procedures.

  24. Will maiLink Router be used to transmit PHI? Please refer to Partner policies and procedures.

  25. Will maiLink Router be used to transmit PII? Please refer to Partner policies and procedures.

SDLC Procedures

  1. Are maiLink Router flaws identified, reported and corrected? Yes.

  2. Are maiLink Router software and firmware updates tested for effectiveness and potential side effects before incorporation? Yes.

  3. Are all maiLink Router configuration changes documented? No.

  4. Are configurable changes to maiLink Router documented? Yes.

  5. Do the documented maiLink Router configuration settings reflect the most restrictive mode consistent with operational requirements? No, not at this time.

  6. Does maiData analyze changes to maiLink Router to determine potential security impacts prior to change implementation. Yes.

  7. Does maiData apply information system security engineering principles in the Product Development Life Cycle of maiLink Router? Yes.

  8. Does maiData approve, control and monitor maiLink Router maintenance tools? Yes.

  9. Does maiData automatically apply software patches to maiLink Router? Yes, using an auto-update mechanism.

  10. Does maiData categorize maiLink Router patches based on severity? Yes, maiData classifies patches as ?minor?, ?major?, and ?critical?.

  11. Does maiData check for potential adverse impact on security controls following maintenance or repair actions? Yes.

  12. Does maiData define a comprehensive life cycle for maiLink Router? No, but it is planned as part of our ISO 27001 process.

  13. Does maiData define the timing of maiLink Router patches? Yes, maiLink Router auto-updates occur within 30 days of release of a maiLink Router release.

  14. **Does maiData develop, document and implement a configuration management plan for maiLink Router that addresses roles, responsibilities and No, but it is planned as part of our ISO 27001 process.

  15. Does maiData have a process for identifying configuration items during the SDLC? Yes.

  16. Does maiData maintain a formal security patch management process for maiLink Router? No, not at this time.

  17. Does maiData maintain documented policies and procedures for maintenance of maiLink Router? Yes.

  18. Does maiData perform vulnerability testing as part of maiLink Router?s Software Development LifeCycle (SDLC)? Yes, using Zap software to test against the Open Web Application Security Project (OWASP) requirements.

  19. Does maiData protect the configuration management plan from unauthorized disclosures and modifications? Not applicable.

  20. Does maiData require maiLink Router developers to conform to maiData-approved configuration changes? Yes.

  21. Does maiData require maiLink Router developers to create and implement a security assessment plan for maiLink Router? No, but maiData is in the process of developing such policies and procedures for conformance with ISO 27001:2013.

  22. Does maiData require the maiLink Router developers security assessment plan to produce evidence of the execution of the security assessment plan? No, but maiData is in the process of developing such policies and procedures for conformance with ISO 27001:2013.

  23. How often does maiData perform penetration tests on maiLink Router? Once per software release.

  24. Is there documentation outlining the baseline configuration of maiLink Router? Yes.

  25. What environments does maiData use in development of patches for maiLink Router? maiData uses Development, QA (including Test) and Production environments to verify and validate patches.

Security

  1. Are strong authenticators/passwords used in the establishment of maiLink Router non-local maintenance and diagnostic sessions? Yes.

  2. Are there any known vulnerabilities within maiLink Router? No.

  3. Can maiLink Router credentials be federated with maiLink SRM? Not at this time.

  4. Does maiLink Router display the last user logon date and time to the user? No.

  5. Does maiLink Router encrypt data at rest? No.

  6. Does maiLink Router encrypt data in transit? Yes, for data transmitted between maiLink Router and Cloud.

  7. Does maiLink Router have a session lock after a period of inactivity that requires reauthentication? Yes, via maiLink SRM.

  8. Does maiLink Router include any collaborative devices (cameras, microphones, etc)? No.

  9. Does maiLink Router limit the number of concurrent sessions for the user? Yes.

  10. Does maiLink Router prevent user actions that can be performed on the system without identification and authentication? Yes.

  11. Does maiLink Router provide system use notification that includes privacy and security notices before granting access? No.

  12. Does maiLink Router separate user functionality from administrative functionality? Yes.

  13. Does maiLink Router store passwords in an encrypted format? Yes.

  14. Does maiLink Router terminate the session after predefined circumstances? Yes, via maiLink SRM.

  15. Does maiLink Router use cryptographic mechanisms to recognize changes to information (such as hashing)? No.

  16. Does maiLink Router use cryptographic protocols to protect transmitted information? Yes.

  17. Does maiLink Router use mechanisms for authentication to a cryptographic module? No.

  18. Does maiData have any automated or manual monitoring of maiLink Router configuration changes? No, not at this time.

  19. How do you do a factory reset on maiLink Router? First reformat the hard drive (writing all zeroes). Then reinstall the maiLink Router ISO file.

  20. How does maiLink Router keep Customers secure? maiLink Router uses maiLink Agent to establish a secure connection to the Cloud using only outbound communications.

  21. Is user installation of maiLink Router restricted and monitored? Yes.

  22. What cryptographic protocols does maiLink Router use to protect transmitted information, including strength? AES 128-bit.

  23. What encryption method does maiLink Router use to encrypt data at rest? None at this time.

  24. What encryption method does maiLink Router use to encrypt data in transit? AES 128-bit.

  25. What is the inactivity period before maiLink Router terminates a session ? 15 minutes.

  26. What was the date of the most recent maiLink Router vulnerability test? 2021-01-29.

Security Policy

  1. Are maiLink Router non-local maintenance and diagnostic sessions terminated after completion? Yes.

  2. Does maiData implement maiLink Router patches categorized as critical within 72 hours of patch release? Yes, depending on the requirement to notify end-user customers of changes associated with a specific patch.

  3. Does maiData maintain a disaster recovery policy which applies to maiLink Router? Yes. maiData security policies apply to all maiData products.

4.1.4 - maiLink SRM

Security information for the maiLink SRM platform.

General

  1. How is maiLink SRM integrated with the customer infrastructure? The Agent software module, built into the Partner’s product, communicates with the maiLink SRM Cloud.

  2. In what year was maiLink SRM first offered? 2022

  3. Is maiLink SRM provided as Software-as-a-Service? Yes.

  4. Is maiLink SRM to be installed in the cloud (not within the customer location)? Yes.

  5. Is maiLink SRM to be locally installed (within the customer location)? No.

  6. What environment does maiLink SRM run in? The Cloud portion of maiLink SRM runs in AWS. The Agent portion runs in the product installed in the customer location.

  7. What is the purpose of maiLink SRM? maiLink SRM is a Service Relationship Management platform that provides Partners with the ability to manage their fleet of deployed devices, track product ownership, and better service the products through integrated, secure remote access.

  8. Who is the best maiData contact for questions or concerns about maiLink SRM security? Adam Zenner, CTO and CISO.

  9. Why would a Customer permit installation of maiLink SRM? Customers that allow use of maiLink SRM enjoy high security, faster service response times, and greater uptime.

Access

  1. Do all Partner employees access maiLink SRM via a single login portal? Yes, via maiLink SRM.

  2. Does maiLink SRM maintain an account lock-out feature, activated after a number of failed login attempts?" Yes, if credentials are federated.

  3. Does maiLink SRM prohibit re-use of prior passwords? Yes, if credentials are federated.

  4. Is each Partner’s implementation accessed via a unique login portal? Yes.

  5. What is the URL for the maiLink SRM? https://app.maidata.io

Audit Logs

  1. Are maiLink SRM audit records time-stamped? Yes.

  2. Are all maiLink SRM non-local maintenance and diagnostic activities approved and monitored? No.

  3. Can maiLink SRM be configured to select which auditable events are captured in the audit log? No.

  4. Do maiLink SRM audit logs contain enough information to establish the identify of the user/subject associated with the event? Yes.

  5. Do maiLink SRM audit logs contain enough information to establish the source of the event? No.

  6. Do maiLink SRM audit logs contain enough information to establish what type of event occurred? Yes, when combined with audit log information in maiLink SRM, to the level of log in / log out.

  7. Do maiLink SRM audit logs contain enough information to establish when the event occurred? Yes.

  8. Do maiLink SRM audit logs contain enough information to establish where the event occurred? No.

  9. Does maiLink SRM allow generation of custom audit reports? Yes, through maiLink SRM.

  10. Does maiLink SRM generate an alert in the event of an audit processing failure? No.

  11. Does maiLink SRM keep audit logs? Yes.

  12. Does maiLink SRM protect audit records from unauthorized access, modification and deletion?" Yes.

  13. Does maiLink SRM record Failed Log In events in its audit logs? No.

  14. Does maiLink SRM record Files / Records Deleted events in its audit logs? No.

  15. Does maiLink SRM record Files / Records Modified events in its audit logs? No.

  16. Does maiLink SRM record Files / Records Viewed events in its audit logs? No.

  17. Does maiLink SRM record Log In events in its audit logs? Yes.

  18. Does maiLink SRM record Log Out events in its audit logs? Yes.

  19. Does generation of a maiLink SRM audit report alter the original content or time stamp of the audit record? No.

  20. Does maiData maintain records for maiLink SRM non-local maintenance and diagnostic sessions? Yes.

  21. Is the information captured in maiLink SRM audit logs sufficient for system and user performance investigations? Yes.

Authentication

  1. Does maiLink SRM protect the authenticity of communication sessions? Yes.

  2. Does maiLink SRM support multi-factor authentication? Yes, if configured.

  3. Does maiLink SRM system obscure the authenticator/password during the authentication process? Yes.

  4. Does maiLink SRM uniquely identify and authenticate devices before establishing communication with the Cloud? Yes, using unique encrypted JSON Web Tokens (JWTs).

  5. Does maiLink SRM uniquely identify and authenticate devices before establishing communication within the Customer facility? Yes.

  6. Does maiLink SRM use managed LDAP services for identification and authentication? No.

  7. How does maiLink SRM achieve MFA? One-Time Password (OTP), if configured.

  8. Is maiLink SRM authenticator content protected from unauthorized disclosure and modification? Yes through encryption.

  9. Is token-based authentication used? Yes.

Configuration

  1. How do you do a factory reset on maiLink SRM? This is not necessary.

  2. How is IP Address of the maiLink SRM configured? Not applicable because maiLink SRM is not on-premise.

  3. How is the configuration of maiLink SRM controlled? maiLink SRM configuration is controlled my maiData. The maiLink SRM configuration for the Partner is controlled by Partner administrators.

Credentials

  1. Can a maiLink SRM user request a password reset? Yes.

  2. Can any user with maiLink SRM credentials add, remove or modify Administrator users? Yes, but that capability will be removed in early 2022.

  3. Can Customer IT personnel be given credentials to maiLink SRM? Yes, but the Partner should not agree to this.

  4. Can the Customer configure the default requirements for passwords? Yes, if federated.

  5. Does maiLink SRM allow federation of credentials? Yes, if credentials are federated.

  6. Does maiLink SRM require passwords to contain at least one non-alphanumeric character? Yes, if credentials are federated.

  7. Does maiLink SRM require passwords to contain at least one numeric digit? Yes, if credentials are federated.

  8. Does maiLink SRM require passwords to contain mixed-case alpha characters? Yes, if credentials are federated.

  9. Does maiLink SRM require passwords to expire every 90 days? Yes, if credentials are federated.

  10. Does maiLink SRM support federated identity? No, but it is planned for a future release.

  11. Must a maiLink SRM user establish their own password at first login? Yes.

  12. What are the minimum requirements for a maiLink SRM password in terms of length and complexity? None at this time unless credentials are federated.

  13. What credentials does a user need to access maiLink SRM locally (from inside the Customer firewall)? There is no local access to maiLink SRM.

  14. What credentials does a user need to access maiLink SRM remotely (from outside the Customer firewall)? Partner-authorized maiLink SRM credentials and role permissions.

Data Access

  1. Will maiLink SRM be used to transmit Customer Employee Information? Only if necessary, authorized and permitted by Customer.

  2. Will maiLink SRM be used to transmit PCI? Only if necessary, authorized and permitted by Customer.

  3. Will maiLink SRM be used to transmit PHI? Only if necessary, authorized and permitted by Customer.

  4. Will maiLink SRM be used to transmit PII? Only if necessary, authorized and permitted by Customer.

  5. Will the Partner be able to access Customer Internal / Proprietary Information via maiLink SRM? Only if necessary, authorized and permitted by Customer.

  6. Will the Partner be able to access Employee Information via maiLink SRM? Only if necessary, authorized and permitted by Customer.

  7. Will the Partner be able to access PCI via maiLink SRM? Only if necessary, authorized and permitted by Customer.

  8. Will the Partner be able to access PHI via maiLink SRM? Only if necessary, authorized and permitted by Customer.

  9. Will the Partner be able to access PII via maiLink SRM? Only if necessary, authorized and permitted by Customer.

  10. Will the Partner be able to receive transmitted data from Customer, via maiLink SRM? Only if necessary, authorized and permitted by Customer.

  11. Will the Partner have access to a database or application, via maiLink SRM, that stores or transmits Customer data? Only if necessary, authorized and permitted by Customer.

  12. Will the Partner have access to infrastructure, via maiLink SRM, that stores or transmits Customer data? Only if necessary, authorized and permitted by Customer.

  13. Will the Partner have access to the Customer network, via maiLink SRM, for on-site support? No.

  14. Will the Partner have access to the Customer network, via maiLink SRM, for remote support? Yes, as authorized and permitted by Customer.

  15. Will the Partner use Customer computer systems to access and/or transmit Customer data via maiLink SRM? No.

  16. Will the Partner use Partner computer systems to access and/or transmit Customer data via maiLink SRM? Yes.

Documentation

  1. Does maiData administrator documentation for maiLink SRM include configuration, installation and operation information? Yes, as applicable.

  2. Does maiData administrator documentation for maiLink SRM include known vulnerabilities regarding configuration and use of administrator functions? Yes, as applicable.

  3. Does maiData administrator documentation for maiLink SRM include security functions and mechanisms information? Yes, as applicable.

  4. Does maiData include requirements, descriptions and criteria in the acquisition contract for maiLink SRM? Yes, but only an abbreviated description. The remaining requirements, descriptions and criteria are in publicly available documents.

  5. Does maiData maintain administrator documentation for maiLink SRM? Yes, as applicable.

  6. Does maiData maintain any documentation which includes the details of maiLink SRM’s security configuration specifications? Yes. Available on request.

  7. Does maiData maintain current accurate documentation of the components in maiLink SRM? Yes.

  8. Does maiData maintain user documentation for maiLink SRM? Yes.

  9. Does maiData user documentation for maiLink SRM include information on methods for user interaction which make maiLink SRM use more secure? Yes, as applicable.

  10. Does maiData user documentation for maiLink SRM include information on user responsibility in maintaining maiLink SRM security? Yes, as applicable.

  11. Does maiData user documentation for maiLink SRM include information on user-accessible security functions and how to use them? Yes, as applicable.

  12. Is any non-local maintenance and diagnostic activity performed on maiLink SRM (E.g. via network)? Yes.

  13. Is there documentation outlining who, when and how maiLink SRM can be configured? Yes.

Partner Responsibilities

  1. Are all approved maiLink SRM configuration changes implemented in a timely manner? Yes.

  2. Does maiData authorize a list of authorized maintenance personnel? No. The Partner authorizes maintenance personnel.

  3. Does maiData ensure that personnel performing maintenance on maiLink SRM have the required access authorizations? Yes.

  4. Does maiData enter access agreements with employees that have access to maiLink SRM? No. It is the responsibility of the Partner to have access agreements with their employees if they are to be authorized to access maiLink SRM.

  5. Does maiData periodically review access agreements for employees that have access to maiLink SRM? Yes. But it is the responsibility of the Partner to periodically review access agreements with their employees if they are authorized to access maiLink SRM.

  6. Does maiData provide remote support / maintenance services that would involve maiData employees accessing maiLink SRM? No, by policy. The Partner may specifically request direct support, and authorize access to the Agent by maiData personnel, however this would not be a common occurrence.

  7. Does maiData screen individuals prior to authorizing access to maiLink SRM? No. It is the responsibility of the Partner to determine which of their employees is authorized to access maiLink SRM.

  8. How will maiLink SRM access, transmit or store Customer’s data? Partner may access, transmit or store Customer’s data using maiLink SRM as a tool. maiLink SRM does not independently access, transmit or store Customer data.

  9. Who can create credentials for maiLink SRM? Partner maiLink SRM Administrator.

Policies and Procedures

  1. Does maiData have personnel sanctions policies and procedures? No, but it is planned as part of our ISO 27001 process.

  2. Does maiData maintain a list of authorized maintenance personnel for maiLink SRM? Yes, maiLink SRM maintains a list of authorized maintenance personnel as authorized by the Partner.

SDLC Procedures

  1. Are maiLink SRM flaws identified, reported and corrected? Yes.

  2. Are maiLink SRM software and firmware updates tested for effectiveness and potential side effects before incorporation? Yes.

  3. Are all maiLink SRM configuration changes documented? Yes.

  4. Are configurable changes to maiLink SRM documented? Yes.

  5. Do the documented maiLink SRM configuration settings reflect the most restrictive mode consistent with operational requirements? Yes.

  6. Does maiData analyze changes to maiLink SRM to determine potential security impacts prior to change implementation. Yes.

  7. Does maiData apply information system security engineering principles in the Product Development Life Cycle of maiLink SRM? Yes.

  8. Does maiData approve, control and monitor maiLink SRM maintenance tools?" Yes.

  9. Does maiData automatically apply software patches to maiLink SRM? Yes, using an auto-update mechanism.

  10. Does maiData categorize maiLink SRM patches based on severity? Yes, maiData classifies patches as “minor”, “major”, and “critical”.

  11. Does maiData check for potential adverse impact on security controls following maintenance or repair actions? Yes.

  12. Does maiData define a comprehensive life cycle for maiLink SRM? No, but it is planned as part of our ISO 27001 process.

  13. Does maiData define the timing of maiLink SRM patches? Yes, maiLink SRM auto-updates occur within 30 days of release of a maiLink SRM release.

  14. Does maiData develop, document and implement a configuration management plan for maiLink SRM that addresses roles, responsibilities and configuration? No, but it is planned as part of our ISO 27001 process.

  15. Does maiData document maiLink SRM configuration changes that deviate from the established settings? Yes.

  16. Does maiData have a process for identifying configuration items during the SDLC? Yes.

  17. Does maiData maintain a formal security patch management process for maiLink SRM? No, not at this time.

  18. Does maiData maintain documented policies and procedures for maintenance of maiLink SRM? Yes.

  19. Does maiData perform vulnerability testing as part of maiLink SRM’s Software Development LifeCycle (SDLC)? Yes, using Zap software to test against the Open Web Application Security Project (OWASP) requirements.

  20. Does maiData protect the configuration management plan from unauthorized disclosures and modifications? Not applicable.

  21. Does maiData require maiLink SRM developers to conform to maiData-approved configuration changes? Yes.

  22. Does maiData require maiLink SRM developers to create and implement a security assessment plan for maiLink SRM? No, but maiData is in the process of developing such policies and procedures for conformance with ISO 27001:2013.

  23. Does maiData require the maiLink SRM developers security assessment plan to produce evidence of the execution of the security assessment plan? No, but maiData is in the process of developing such policies and procedures for conformance with ISO 27001:2013.

  24. How often does maiData perform penetration tests on maiLink SRM? Once per software release.

  25. Is there active monitoring of maiLink SRM configuration changes? No.

  26. Is there documentation outlining the baseline configuration of maiLink SRM? No.

  27. What environments does maiData use in development of patches for maiLink SRM? maiData uses our QA environment to verify and validate patches.

Security

  1. Are strong authenticators/passwords used in the establishment of maiLink SRM non-local maintenance and diagnostic sessions? Yes.

  2. Are there any known vulnerabilities within maiLink SRM? No.

  3. Can maiLink SRM credentials be federated with Partner’s IT credentialing authority? Yes, with SAML.

  4. Does maiLink SRM come with its own antivirus solution? Not applicable.

  5. Does maiLink SRM come with its own malware protection? Not applicable.

  6. Does maiLink SRM display the last user logon date and time to the user? No.

  7. Does maiLink SRM encrypt data at rest? Yes.

  8. Does maiLink SRM encrypt data in transit? Yes.

  9. Does maiLink SRM have a session lock after a period of inactivity that requires reauthentication? Yes.

  10. Does maiLink SRM include any collaborative devices (cameras, microphones, etc)? No.

  11. Does maiLink SRM limit the number of concurrent sessions for the user? No.

  12. Does maiLink SRM prevent user actions that can be performed on the system without identification and authentication? Yes.

  13. Does maiLink SRM provide system use notification that includes privacy and security notices before granting access? No.

  14. Does maiLink SRM separate user functionality from administrative functionality? Yes.

  15. Does maiLink SRM store passwords in an encrypted format? Yes.

  16. Does maiLink SRM terminate the session after predefined circumstances? Yes.

  17. Does maiLink SRM use cryptographic mechanisms to recognize changes to information (such as hashing)? No.

  18. Does maiLink SRM use cryptographic protocols to protect transmitted information? Yes.

  19. Does maiLink SRM use managed LDAP services for identification and authentication? Yes, if configured.

  20. Does maiLink SRM use mechanisms for authentication to a cryptographic module? No.

  21. Does maiData have any automated or manual monitoring of maiLink SRM configuration changes? No, not at this time.

  22. Does maiData have termination procedures in place for those with access to maiLink SRM? _Yes, but is the responsibility of the Partner to handle the termination of any of their own employees that are authorized to access maiLink SRM. _ **Does maiData have third-party access control procedures for external parties granted access to maiLink SRM?**1. Yes. maiData does not grant third-party access to maiLink SRM.

  23. Does maiData have transfer procedures in place for those with access to maiLink SRM? No. It is the responsibility of the Partner to handle transfer between employees of authorization to access maiLink SRM.

  24. Does maiData restrict or prohibit the use of any maiLink SRM functions, ports, protocols and/or service that are not essential? Yes.

  25. Does maiData retain records of maiLink SRM configuration changes? Yes.

  26. Does maiData review proposed maiLink SRM configuration changes using defined security impact analyses? No.

  27. Has maiLink SRM undergone any major platform changes, upgrades or enhancements in the past six months? No.

  28. How does maiLink SRM keep Customers secure? The user federation available in maiLink SRM ensures that the only Partner-approved service techinicians have access to the products in the customer facility. In addition, the Agent software built into each product only uses outbound ports to connect with maiLink SRM.

  29. Is user installation of maiLink SRM restricted and monitored? Yes.

  30. What cryptographic protocols does maiLink SRM use to protect transmitted information, including strength? AES 128-bit.

  31. What encryption method does maiLink SRM use to encrypt data at rest? No.

  32. What encryption method does maiLink SRM use to encrypt data in transit? AES 128-bit.

  33. What is the inactivity period before maiLink SRM terminates a session? 15 minutes.

  34. What was the date of the most recent maiLink SRM vulnerability test? 2021-01-29.

Security Policy

  1. Are maiLink SRM non-local maintenance and diagnostic sessions terminated after completion? Yes.

  2. Does maiData implement maiLink SRM patches categorized as critical within 72 hours of patch release? Yes, depending on the requirement to notify end-user customers of changes associated with a specific patch.

  3. Does maiData maintain a disaster recovery policy which applies to maiLink SRM? No, but it is planned as part of our ISO 27001 process.

5 - License Disclosures

Licenses for dependencies used in maiLink
Name License URL
@aws-sdk/client-s3@3.178.0 Apache-2.0 https://github.com/aws/aws-sdk-js-v3
@babel/core@7.19.1 MIT https://github.com/babel/babel
@babel/runtime@7.19.0 MIT https://github.com/babel/babel
@emotion/react@11.10.4 MIT https://github.com/emotion-js/emotion.git#main
@emotion/styled@11.10.4 MIT https://github.com/emotion-js/emotion.git#main
@firebase/app-types@0.7.0 Apache-2.0 https://github.com/firebase/firebase-js-sdk
@firebase/util@1.6.3 Apache-2.0 https://github.com/firebase/firebase-js-sdk
@mui/base@5.0.0-alpha.98 MIT https://github.com/mui/material-ui
@mui/icons-material@5.10.6 MIT https://github.com/mui/material-ui
@mui/lab@5.0.0-alpha.100 MIT https://github.com/mui/material-ui
@mui/material@5.10.6 MIT https://github.com/mui/material-ui
@mui/styles@5.10.6 MIT https://github.com/mui/material-ui
@mui/system@5.10.6 MIT https://github.com/mui/material-ui
@mui/x-data-grid@5.17.4 MIT https://github.com/mui/mui-x
@reduxjs/toolkit@1.8.5 MIT https://github.com/reduxjs/redux-toolkit
@types/ua-parser-js@0.7.36 MIT https://github.com/DefinitelyTyped/DefinitelyTyped
@vitejs/plugin-react-refresh@1.3.6 MIT https://github.com/vitejs/vite
axios@0.21.4 MIT https://github.com/axios/axios
buffer@6.0.3 MIT https://github.com/feross/buffer
ch.qos.logback:logback-classic:1.2.11 EPL-1.0) (GNU Lesser General Public License http://logback.qos.ch/logback-classic
ch.qos.logback:logback-core:1.2.11 EPL-1.0) (GNU Lesser General Public License http://logback.qos.ch/logback-core
chart.js@3.9.1 MIT https://github.com/chartjs/Chart.js
chartjs-adapter-luxon@1.2.0 MIT https://github.com/chartjs/chartjs-adapter-luxon
chartjs-plugin-annotation@2.0.1 MIT https://github.com/chartjs/chartjs-plugin-annotation
com.auth0:java-jwt:3.19.2 MIT https://github.com/auth0/java-jwt
com.github.kevinstern:software-and-algorithms:1.0 MIT https://www.github.com/KevinStern/software-and-algorithms
com.github.spotbugs:spotbugs-annotations:4.7.1 LGPL-2.1 https://spotbugs.github.io/
com.google.api:api-common:2.2.0 BSD-3-Clause https://github.com/googleapis/api-common-java
com.google.api:gax-grpc:2.18.1 BSD-3-Clause) GAX (Google Api eXtensions https://github.com/googleapis/gax-java
com.google.api:gax-httpjson:0.103.1 BSD-3-Clause) GAX (Google Api eXtensions https://github.com/googleapis/gax-java
com.google.api:gax:2.18.1 BSD-3-Clause) GAX (Google Api eXtensions https://github.com/googleapis/gax-java
com.google.auth:google-auth-library-credentials:1.11.0 BSD-3-Clause https://github.com/googleapis/google-auth-library-java/google-auth-library-credentials
com.google.auth:google-auth-library-credentials:1.7.0 BSD-3-Clause https://github.com/googleapis/google-auth-library-java/google-auth-library-credentials
com.google.auth:google-auth-library-oauth2-http:1.11.0 BSD-3-Clause https://github.com/googleapis/google-auth-library-java/google-auth-library-oauth2-http
com.google.code.findbugs:jFormatString:3.0.0 GNU Lesser Public License http://findbugs.sourceforge.net/
com.google.errorprone:javac:9+181-r4173-1 GNU General Public License, version 2, with the Classpath Exception https://github.com/google/error-prone-javac
com.google.protobuf:protobuf-java-util:3.20.1 BSD-3-Clause https://developers.google.com/protocol-buffers/protobuf-java-util/
com.google.protobuf:protobuf-java:3.20.1 BSD-3-Clause https://developers.google.com/protocol-buffers/protobuf-java/
com.google.re2j:re2j:1.5 Go License http://github.com/google/re2j
com.hubspot.rosetta:RosettaAnnotations:3.11.10 Apache-2.0 license no url defined
com.hubspot.rosetta:RosettaCore:3.11.10 Apache-2.0 license no url defined
com.hubspot.rosetta:RosettaJdbi3:3.11.10 Apache-2.0 license no url defined
com.meilisearch.sdk:meilisearch-java:0.7.2 MIT https://github.com/meilisearch/meilisearch-java
com.rabbitmq:amqp-client:5.16.0 AL 2.0) (GPL v2) (MPL 2.0 https://www.rabbitmq.com
com.sendgrid:java-http-client:4.5.0 MIT https://github.com/sendgrid/java-http-client
com.sendgrid:sendgrid-java:4.9.3 MIT https://github.com/sendgrid/sendgrid-java
com.vladsch.flexmark:flexmark-ext-emoji:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-ext-emoji
com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-ext-gfm-strikethrough
com.vladsch.flexmark:flexmark-ext-ins:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-ext-ins
com.vladsch.flexmark:flexmark-ext-superscript:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-ext-superscript
com.vladsch.flexmark:flexmark-ext-tables:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-ext-tables
com.vladsch.flexmark:flexmark-ext-wikilink:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-ext-wikilink
com.vladsch.flexmark:flexmark-html2md-converter:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-html2md-converter
com.vladsch.flexmark:flexmark-jira-converter:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-jira-converter
com.vladsch.flexmark:flexmark-util-ast:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-util-ast
com.vladsch.flexmark:flexmark-util-builder:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-util-builder
com.vladsch.flexmark:flexmark-util-collection:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-util-collection
com.vladsch.flexmark:flexmark-util-data:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-util-data
com.vladsch.flexmark:flexmark-util-dependency:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-util-dependency
com.vladsch.flexmark:flexmark-util-format:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-util-format
com.vladsch.flexmark:flexmark-util-html:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-util-html
com.vladsch.flexmark:flexmark-util-misc:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-util-misc
com.vladsch.flexmark:flexmark-util-options:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-util-options
com.vladsch.flexmark:flexmark-util-sequence:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-util-sequence
com.vladsch.flexmark:flexmark-util-visitor:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-util-visitor
com.vladsch.flexmark:flexmark-util:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark-util
com.vladsch.flexmark:flexmark:0.62.2 BSD-3-Clause https://github.com/vsch/flexmark-java/flexmark
commonninja-react@1.0.5 ISC https://github.com/CommonNinja/commonninja-react
copy-to-clipboard@3.3.2 MIT https://github.com/sudodoki/copy-to-clipboard
d3@7.6.1 ISC https://github.com/d3/d3
esbuild@0.16.1 MIT https://github.com/evanw/esbuild
filesize@10.0.5 BSD-3-Clause https://github.com/avoidwork/filesize.js
firebase@8.10.1 Apache-2.0 https://github.com/firebase/firebase-js-sdk
formik@2.2.9 Apache-2.0 https://github.com/formium/formik
github.com/ProtonMail/go-crypto@v0.0.0-20220824120805-4b6e5c587895 BSD-3-Clause https://github.com/ProtonMail/go-crypto/blob/4b6e5c587895/LICENSE
github.com/alecthomas/kong@v0.6.1 MIT https://github.com/alecthomas/kong/blob/v0.6.1/COPYING
github.com/antonmedv/expr@v1.9.1-0.20221112134005-497c6bd01cf8 MIT https://github.com/antonmedv/expr/blob/497c6bd01cf8/LICENSE
github.com/atotto/clipboard@v0.1.4 BSD-3-Clause https://github.com/atotto/clipboard/blob/v0.1.4/LICENSE
github.com/aymanbagabas/go-osc52@v1.0.3 MIT https://github.com/aymanbagabas/go-osc52/blob/v1.0.3/LICENSE
github.com/cenkalti/backoff/v4@v4.1.3 MIT https://github.com/cenkalti/backoff/blob/v4.1.3/LICENSE
github.com/charmbracelet/bubbles@v0.14.0 MIT https://github.com/charmbracelet/bubbles/blob/v0.14.0/LICENSE
github.com/charmbracelet/bubbletea@v0.22.1 MIT https://github.com/charmbracelet/bubbletea/blob/v0.22.1/LICENSE
github.com/charmbracelet/lipgloss@v0.6.0 MIT https://github.com/charmbracelet/lipgloss/blob/v0.6.0/LICENSE
github.com/cloudflare/circl@v1.2.0 BSD-3-Clause https://github.com/cloudflare/circl/blob/v1.2.0/LICENSE
github.com/containerd/console@v1.0.3 Apache-2.0 https://github.com/containerd/console/blob/v1.0.3/LICENSE
github.com/davecgh/go-spew/spew@v1.1.1 ISC https://github.com/davecgh/go-spew/blob/v1.1.1/LICENSE
github.com/emirpasic/gods@v1.18.1 BSD-2-Clause https://github.com/emirpasic/gods/blob/v1.18.1/LICENSE
github.com/fatih/color@v1.13.0 MIT https://github.com/fatih/color/blob/v1.13.0/LICENSE.md
github.com/fsnotify/fsnotify@v1.5.4 BSD-3-Clause https://github.com/fsnotify/fsnotify/blob/v1.5.4/LICENSE
github.com/go-git/gcfg@v1.5.0 BSD-3-Clause https://github.com/go-git/gcfg/blob/v1.5.0/LICENSE
github.com/go-git/go-billy/v5@v5.3.1 Apache-2.0 https://github.com/go-git/go-billy/blob/v5.3.1/LICENSE
github.com/go-git/go-git/v5@v5.4.2 Apache-2.0 https://github.com/go-git/go-git/blob/v5.4.2/LICENSE
github.com/go-logr/logr@v1.2.3 Apache-2.0 https://github.com/go-logr/logr/blob/v1.2.3/LICENSE
github.com/gogo/protobuf@v1.3.2 BSD-3-Clause https://github.com/gogo/protobuf/blob/v1.3.2/LICENSE
github.com/golang-jwt/jwt@v3.2.2 MIT https://github.com/golang-jwt/jwt/blob/v3.2.2/LICENSE
github.com/golang/protobuf@v1.5.2 BSD-3-Clause https://github.com/golang/protobuf/blob/v1.5.2/LICENSE
github.com/google/gofuzz@v1.2.0 Apache-2.0 https://github.com/google/gofuzz/blob/v1.2.0/LICENSE
github.com/google/tink/go@v1.7.0 Apache-2.0 https://github.com/google/tink/blob/go/v1.7.0/go/LICENSE
github.com/google/uuid@v1.3.0 BSD-3-Clause https://github.com/google/uuid/blob/v1.3.0/LICENSE
github.com/google/wire@v0.5.0 Apache-2.0 https://github.com/google/wire/blob/v0.5.0/LICENSE
github.com/hashicorp/errwrap@v1.1.0 MPL-2.0 https://github.com/hashicorp/errwrap/blob/v1.1.0/LICENSE
github.com/hashicorp/go-cleanhttp@v0.5.2 MPL-2.0 https://github.com/hashicorp/go-cleanhttp/blob/v0.5.2/LICENSE
github.com/hashicorp/go-hclog@v1.3.1 MIT https://github.com/hashicorp/go-hclog/blob/v1.3.1/LICENSE
github.com/hashicorp/go-multierror@v1.1.1 MPL-2.0 https://github.com/hashicorp/go-multierror/blob/v1.1.1/LICENSE
github.com/hashicorp/go-plugin@v1.4.5 MPL-2.0 https://github.com/hashicorp/go-plugin/blob/v1.4.5/LICENSE
github.com/hashicorp/go-retryablehttp@v0.7.1 MPL-2.0 https://github.com/hashicorp/go-retryablehttp/blob/v0.7.1/LICENSE
github.com/hashicorp/yamux@v0.1.1 MPL-2.0 https://github.com/hashicorp/yamux/blob/v0.1.1/LICENSE
github.com/imdario/mergo@v0.3.13 BSD-3-Clause https://github.com/imdario/mergo/blob/v0.3.13/LICENSE
github.com/jackc/chunkreader/v2@v2.0.1 MIT https://github.com/jackc/chunkreader/blob/v2.0.1/LICENSE
github.com/jackc/pgconn@v1.13.0 MIT https://github.com/jackc/pgconn/blob/v1.13.0/LICENSE
github.com/jackc/pgio@v1.0.0 MIT https://github.com/jackc/pgio/blob/v1.0.0/LICENSE
github.com/jackc/pgpassfile@v1.0.0 MIT https://github.com/jackc/pgpassfile/blob/v1.0.0/LICENSE
github.com/jackc/pgproto3/v2@v2.3.1 MIT https://github.com/jackc/pgproto3/blob/v2.3.1/LICENSE
github.com/jackc/pgservicefile@v0.0.0-20200714003250-2b9c44734f2b MIT https://github.com/jackc/pgservicefile/blob/2b9c44734f2b/LICENSE
github.com/jackc/pgtype@v1.12.0 MIT https://github.com/jackc/pgtype/blob/v1.12.0/LICENSE
github.com/jackc/pgx/v4@v4.17.2 MIT https://github.com/jackc/pgx/blob/v4.17.2/LICENSE
github.com/jbenet/go-context/io@v0.0.0-20150711004518-d14ea06fba99 MIT https://github.com/jbenet/go-context/blob/d14ea06fba99/LICENSE
github.com/json-iterator/go@v1.1.12 MIT https://github.com/json-iterator/go/blob/v1.1.12/LICENSE
github.com/kardianos/service@v1.2.1 Zlib https://github.com/kardianos/service/blob/v1.2.1/LICENSE
github.com/kevinburke/ssh_config@v1.2.0 MIT https://github.com/kevinburke/ssh_config/blob/v1.2.0/LICENSE
github.com/klauspost/compress/internal/snapref@v1.15.10 BSD-3-Clause https://github.com/klauspost/compress/blob/v1.15.10/internal/snapref/LICENSE
github.com/klauspost/compress/s2@v1.15.10 BSD-3-Clause https://github.com/klauspost/compress/blob/v1.15.10/s2/LICENSE
github.com/klauspost/compress/snappy@v1.15.10 BSD-3-Clause https://github.com/klauspost/compress/blob/v1.15.10/snappy/LICENSE
github.com/klauspost/compress/zstd/internal/xxhash@v1.15.10 MIT https://github.com/klauspost/compress/blob/v1.15.10/zstd/internal/xxhash/LICENSE.txt
github.com/klauspost/compress@v1.15.10 Apache-2.0 https://github.com/klauspost/compress/blob/v1.15.10/LICENSE
github.com/labstack/echo/v4@v4.9.0 MIT https://github.com/labstack/echo/blob/v4.9.0/LICENSE
github.com/labstack/gommon@v0.3.1 MIT https://github.com/labstack/gommon/blob/v0.3.1/LICENSE
github.com/lib/pq@v1.10.4 MIT https://github.com/lib/pq/blob/v1.10.4/LICENSE.md
github.com/lucasb-eyer/go-colorful@v1.2.0 MIT https://github.com/lucasb-eyer/go-colorful/blob/v1.2.0/LICENSE
github.com/mattn/go-colorable@v0.1.13 MIT https://github.com/mattn/go-colorable/blob/v0.1.13/LICENSE
github.com/mattn/go-isatty@v0.0.16 MIT https://github.com/mattn/go-isatty/blob/v0.0.16/LICENSE
github.com/mattn/go-localereader@v0.0.2-0.20220822084749-2491eb6c1c75 MIT https://github.com/mattn/go-localereader/blob/2491eb6c1c75/LICENSE
github.com/mattn/go-runewidth@v0.0.14 MIT https://github.com/mattn/go-runewidth/blob/v0.0.14/LICENSE
github.com/minio/highwayhash@v1.0.2 Apache-2.0 https://github.com/minio/highwayhash/blob/v1.0.2/LICENSE
github.com/mitchellh/go-homedir@v1.1.0 MIT https://github.com/mitchellh/go-homedir/blob/v1.1.0/LICENSE
github.com/mitchellh/go-testing-interface@v1.14.1 MIT https://github.com/mitchellh/go-testing-interface/blob/v1.14.1/LICENSE
github.com/modern-go/concurrent@v0.0.0-20180306012644-bacd9c7ef1dd Apache-2.0 https://github.com/modern-go/concurrent/blob/bacd9c7ef1dd/LICENSE
github.com/modern-go/reflect2@v1.0.2 Apache-2.0 https://github.com/modern-go/reflect2/blob/v1.0.2/LICENSE
github.com/muesli/ansi@v0.0.0-20211031195517-c9f0611b6c70 MIT https://github.com/muesli/ansi/blob/c9f0611b6c70/LICENSE
github.com/muesli/cancelreader@v0.2.2 MIT https://github.com/muesli/cancelreader/blob/v0.2.2/LICENSE
github.com/muesli/reflow@v0.3.0 MIT https://github.com/muesli/reflow/blob/v0.3.0/LICENSE
github.com/muesli/termenv@v0.13.0 MIT https://github.com/muesli/termenv/blob/v0.13.0/LICENSE
github.com/nats-io/jwt/v2@v2.3.0 Apache-2.0 https://github.com/nats-io/jwt/blob/v2.3.0/v2/LICENSE
github.com/nats-io/nats-server/v2@v2.9.1 Apache-2.0 https://github.com/nats-io/nats-server/blob/v2.9.1/LICENSE
github.com/nats-io/nats.go@v1.17.0 Apache-2.0 https://github.com/nats-io/nats.go/blob/v1.17.0/LICENSE
github.com/nats-io/nkeys@v0.3.0 Apache-2.0 https://github.com/nats-io/nkeys/blob/v0.3.0/LICENSE
github.com/nats-io/nuid@v1.0.1 Apache-2.0 https://github.com/nats-io/nuid/blob/v1.0.1/LICENSE
github.com/oklog/run@v1.1.0 Apache-2.0 https://github.com/oklog/run/blob/v1.1.0/LICENSE
github.com/pbnjay/memory@v0.0.0-20210728143218-7b4eea64cf58 BSD-3-Clause https://github.com/pbnjay/memory/blob/7b4eea64cf58/LICENSE
github.com/pkg/errors@v0.9.1 BSD-2-Clause https://github.com/pkg/errors/blob/v0.9.1/LICENSE
github.com/posener/complete@v1.2.3 MIT https://github.com/posener/complete/blob/v1.2.3/LICENSE.txt
github.com/prometheus/prometheus/prompb@v0.38.0 Apache-2.0 https://github.com/prometheus/prometheus/blob/v0.38.0/LICENSE
github.com/rabbitmq/amqp091-go@v1.5.0 BSD-2-Clause https://github.com/rabbitmq/amqp091-go/blob/v1.5.0/LICENSE
github.com/rivo/uniseg@v0.4.2 MIT https://github.com/rivo/uniseg/blob/v0.4.2/LICENSE.txt
github.com/riywo/loginshell@v0.0.0-20200815045211-7d26008be1ab MIT https://github.com/riywo/loginshell/blob/7d26008be1ab/LICENSE
github.com/sahilm/fuzzy@v0.1.0 MIT https://github.com/sahilm/fuzzy/blob/v0.1.0/LICENSE
github.com/segmentio/fasthash/fnv1a@v1.0.3 MIT https://github.com/segmentio/fasthash/blob/v1.0.3/LICENSE
github.com/sergi/go-diff/diffmatchpatch@v1.2.0 MIT https://github.com/sergi/go-diff/blob/v1.2.0/LICENSE
github.com/shirou/gopsutil/v3@v3.22.9 BSD-3-Clause https://github.com/shirou/gopsutil/blob/v3.22.9/LICENSE
github.com/sirupsen/logrus@v1.9.0 MIT https://github.com/sirupsen/logrus/blob/v1.9.0/LICENSE
github.com/spf13/pflag@v1.0.5 BSD-3-Clause https://github.com/spf13/pflag/blob/v1.0.5/LICENSE
github.com/tklauser/go-sysconf@v0.3.10 BSD-3-Clause https://github.com/tklauser/go-sysconf/blob/v0.3.10/LICENSE
github.com/tklauser/numcpus@v0.5.0 Apache-2.0 https://github.com/tklauser/numcpus/blob/v0.5.0/LICENSE
github.com/upper/db/v4/internal/reflectx@v4.6.0 MIT https://github.com/upper/db/blob/v4.6.0/internal/reflectx/LICENSE
github.com/upper/db/v4@v4.6.0 MIT https://github.com/upper/db/blob/v4.6.0/LICENSE
github.com/valyala/bytebufferpool@v1.0.0 MIT https://github.com/valyala/bytebufferpool/blob/v1.0.0/LICENSE
github.com/valyala/fasttemplate@v1.2.1 MIT https://github.com/valyala/fasttemplate/blob/v1.2.1/LICENSE
github.com/wagslane/go-rabbitmq@v0.10.0 MIT https://github.com/wagslane/go-rabbitmq/blob/v0.10.0/LICENSE
github.com/willabides/kongplete@v0.3.0 MIT https://github.com/willabides/kongplete/blob/v0.3.0/LICENSE
github.com/xanzy/ssh-agent@v0.3.2 Apache-2.0 https://github.com/xanzy/ssh-agent/blob/v0.3.2/LICENSE
golang.org/x/crypto@v0.0.0-20220919173607-35f4265a4bc0 BSD-3-Clause https://cs.opensource.google/go/x/crypto/+/35f4265a:LICENSE
golang.org/x/mod/semver@v0.6.0-dev.0.20220419223038-86c51ed26bb4 BSD-3-Clause https://cs.opensource.google/go/x/mod/+/86c51ed2:LICENSE
golang.org/x/net@v0.0.0-20220921203646-d300de134e69 BSD-3-Clause https://cs.opensource.google/go/x/net/+/d300de13:LICENSE
golang.org/x/oauth2@v0.0.0-20220909003341-f21342109be1 BSD-3-Clause https://cs.opensource.google/go/x/oauth2/+/f2134210:LICENSE
golang.org/x/sync@v0.0.0-20220907140024-f12130a52804 BSD-3-Clause https://cs.opensource.google/go/x/sync/+/f12130a5:LICENSE
golang.org/x/sys@v0.0.0-20220919091848-fb04ddd9f9c8 BSD-3-Clause https://cs.opensource.google/go/x/sys/+/fb04ddd9:LICENSE
golang.org/x/term@v0.0.0-20220919170432-7a66f970e087 BSD-3-Clause https://cs.opensource.google/go/x/term/+/7a66f970:LICENSE
golang.org/x/text@v0.3.7 BSD-3-Clause https://cs.opensource.google/go/x/text/+/v0.3.7:LICENSE
golang.org/x/time/rate@v0.0.0-20220922220347-f3bd1da661af BSD-3-Clause https://cs.opensource.google/go/x/time/+/f3bd1da6:LICENSE
golang.org/x/tools@v0.1.12 BSD-3-Clause https://cs.opensource.google/go/x/tools/+/v0.1.12:LICENSE
google.golang.org/genproto/googleapis/rpc/status@v0.0.0-20220921223823-23cae91e6737 Apache-2.0 https://github.com/googleapis/go-genproto/blob/23cae91e6737/LICENSE
google.golang.org/grpc@v1.49.0 Apache-2.0 https://github.com/grpc/grpc-go/blob/v1.49.0/LICENSE
google.golang.org/protobuf@v1.28.1 BSD-3-Clause https://github.com/protocolbuffers/protobuf-go/blob/v1.28.1/LICENSE
gopkg.in/inf.v0@v0.9.1 BSD-3-Clause https://github.com/go-inf/inf/blob/v0.9.1/LICENSE
gopkg.in/natefinch/lumberjack.v2@v2.0.0 MIT https://github.com/natefinch/lumberjack/blob/v2.0.0/LICENSE
gopkg.in/warnings.v0@v0.1.2 BSD-2-Clause https://github.com/go-warnings/warnings/blob/v0.1.2/LICENSE
gopkg.in/yaml.v2@v2.4.0 Apache-2.0 https://github.com/go-yaml/yaml/blob/v2.4.0/LICENSE
husky@7.0.4 MIT https://github.com/typicode/husky
immer@9.0.16 MIT https://github.com/immerjs/immer
io.sentry:sentry-logback:6.4.2 MIT https://github.com/getsentry/sentry-java
io.sentry:sentry:6.4.2 MIT https://github.com/getsentry/sentry-java
jakarta.activation:jakarta.activation-api:1.2.1 EDL 1.0 https://github.com/eclipse-ee4j/jaf/jakarta.activation-api
jakarta.annotation:jakarta.annotation-api:2.1.1 EPL-2.0) (GPL2 w/ CPE https://projects.eclipse.org/projects/ee4j.ca
jakarta.xml.bind:jakarta.xml.bind-api:2.3.2 Eclipse Distribution License - v 1.0 https://github.com/eclipse-ee4j/jaxb-api/jakarta.xml.bind-api
javax.activation:javax.activation-api:1.2.0 CDDL/GPLv2+CE http://java.net/all/javax.activation-api/
javax.annotation:javax.annotation-api:1.3.2 CDDL + GPLv2 with classpath exception http://jcp.org/en/jsr/detail?id=250
javax.xml.bind:jaxb-api:2.3.1 CDDL 1.1) (GPL2 w/ CPE https://github.com/javaee/jaxb-spec/jaxb-api
k8s.io/apimachinery/pkg@v0.25.2 Apache-2.0 https://github.com/kubernetes/apimachinery/blob/v0.25.2/LICENSE
k8s.io/apimachinery/third_party/forked/golang/reflect@v0.25.2 BSD-3-Clause https://github.com/kubernetes/apimachinery/blob/v0.25.2/third_party/forked/golang/LICENSE
k8s.io/client-go@v0.25.2 Apache-2.0 https://github.com/kubernetes/client-go/blob/v0.25.2/LICENSE
k8s.io/klog/v2@v2.80.1 Apache-2.0 https://github.com/kubernetes/klog/blob/v2.80.1/LICENSE
k8s.io/utils/internal/third_party/forked/golang/net@v0.0.0-20220922133306-665eaaec4324 BSD-3-Clause https://github.com/kubernetes/utils/blob/665eaaec4324/internal/third_party/forked/golang/LICENSE
k8s.io/utils@v0.0.0-20220922133306-665eaaec4324 Apache-2.0 https://github.com/kubernetes/utils/blob/665eaaec4324/LICENSE
leaflet@1.9.2 BSD-2-Clause https://github.com/Leaflet/Leaflet
lodash@4.17.21 MIT https://github.com/lodash/lodash
luxon@3.0.4 MIT https://github.com/moment/luxon
moment-timezone@0.5.37 MIT https://github.com/moment/moment-timezone
moment@2.29.4 MIT https://github.com/moment/moment
monaco-editor@0.34.1 MIT https://github.com/microsoft/monaco-editor
monaco-promql@1.7.4 MIT https://github.com/prometheus-community/monaco-promql
net.i2p.crypto:eddsa:0.3.0 CC0 1.0 Universal https://github.com/str4d/ed25519-java
notistack@2.0.5 MIT https://github.com/iamhosseindhv/notistack
org.bouncycastle:bcpkix-jdk15on:1.67 Bouncy Castle Licence http://www.bouncycastle.org/java.html
org.bouncycastle:bcprov-jdk15on:1.70 Bouncy Castle Licence https://www.bouncycastle.org/java.html
org.checkerframework:checker-qual:3.12.0 MIT https://checkerframework.org
org.checkerframework:checker-qual:3.22.0 MIT https://checkerframework.org
org.checkerframework:checker-qual:3.5.0 MIT https://checkerframework.org
org.checkerframework:dataflow-errorprone:3.15.0 GNU General Public License, version 2 (GPL2), with the classpath exception) Dataflow (errorprone https://checkerframework.org
org.codehaus.mojo:animal-sniffer-annotations:1.21 MIT https://www.mojohaus.org/animal-sniffer/animal-sniffer-annotations
org.eclipse.jgit:org.eclipse.jgit:4.4.1.201607150455-r Eclipse Distribution License (New BSD License) http://www.eclipse.org/jgit//org.eclipse.jgit
org.hdrhistogram:HdrHistogram:2.1.12 BSD-2-Clause) (Public Domain, per Creative Commons CC0 http://hdrhistogram.github.io/HdrHistogram/
org.json:json:20220320 The JSON License https://github.com/douglascrockford/JSON-java
org.jsoup:jsoup:1.11.3 MIT https://jsoup.org/
org.junit.jupiter:junit-jupiter-api:5.9.0 EPL-2.0 https://junit.org/junit5/
org.junit.jupiter:junit-jupiter-engine:5.9.0 EPL-2.0 https://junit.org/junit5/
org.junit.jupiter:junit-jupiter-params:5.9.0 EPL-2.0 https://junit.org/junit5/
org.junit.jupiter:junit-jupiter:5.9.0 EPL-2.0) JUnit Jupiter (Aggregator https://junit.org/junit5/
org.junit.platform:junit-platform-commons:1.9.0 EPL-2.0 https://junit.org/junit5/
org.junit.platform:junit-platform-engine:1.9.0 EPL-2.0 https://junit.org/junit5/
org.latencyutils:LatencyUtils:2.0.3 Public Domain, per Creative Commons CC0 http://latencyutils.github.io/LatencyUtils/
org.mockito:mockito-core:4.8.0 MIT https://github.com/mockito/mockito
org.mockito:mockito-junit-jupiter:4.8.0 MIT https://github.com/mockito/mockito
org.pcollections:pcollections:2.1.2 MIT http://pcollections.org
org.postgresql:postgresql:42.5.0 BSD-2-Clause https://jdbc.postgresql.org
org.reactivestreams:reactive-streams:1.0.4 MIT http://www.reactive-streams.org/
org.slf4j:slf4j-api:1.7.36 MIT http://www.slf4j.org
org.threeten:threetenbp:1.6.0 BSD-3-Clause https://www.threeten.org/threetenbp
prettier@2.7.1 MIT https://github.com/prettier/prettier
prop-types@15.8.1 MIT https://github.com/facebook/prop-types
react-chartjs-2@4.3.1 MIT https://github.com/reactchartjs/react-chartjs-2
react-dom@17.0.2 MIT https://github.com/facebook/react
react-idle-timer@4.6.4 MIT https://github.com/supremetechnopriest/react-idle-timer
react-leaflet@3.2.5 Hippocratic-2.1 https://github.com/PaulLeCam/react-leaflet
react-redux@7.2.9 MIT https://github.com/reduxjs/react-redux
react-router-dom@5.3.3 MIT https://github.com/remix-run/react-router
react-router@5.3.3 MIT https://github.com/remix-run/react-router
react@17.0.2 MIT https://github.com/facebook/react
reactfire@3.0.0 MIT https://github.com/FirebaseExtended/reactfire
reselect@4.1.6 MIT https://github.com/reduxjs/reselect
sigs.k8s.io/json@v0.0.0-20220713155537-f223a00ba0e2 Apache-2.0 https://github.com/kubernetes-sigs/json/blob/f223a00ba0e2/LICENSE
sigs.k8s.io/structured-merge-diff/v4/value@v4.2.3 Apache-2.0 https://github.com/kubernetes-sigs/structured-merge-diff/blob/v4.2.3/LICENSE
sigs.k8s.io/yaml@v1.3.0 MIT https://github.com/kubernetes-sigs/yaml/blob/v1.3.0/LICENSE
ua-parser-js@1.0.2 MIT https://github.com/faisalman/ua-parser-js
uuid@8.3.2 MIT https://github.com/uuidjs/uuid
vite@3.1.3 MIT https://github.com/vitejs/vite
web-vitals@1.1.2 Apache-2.0 https://github.com/GoogleChrome/web-vitals
yup@0.32.11 MIT https://github.com/jquense/yup