Skip to content

Applications SDK

The Normal SDK is a runtime framework which provides the ability to run managed node JavaScript code within a Normal instance. This can be used for integrations with other APIs, data import/export, control sequences, analytics, and many other use cases.

Applications are stored in git repositories, and can be installed directly from that repository, making it easy to share and update applications. They also embed static pages with access to the local Normal APIs in order to provide dashboards or other UIs.

Hooks

An application is made up of a group of serverless functions called hooks, that implement the logic. The hooks run in a managed JavaScript environment, so they can access local data via the Normal APIs, use packages from NPM, or connect to external services subject to the security policy.

Normal provides support for managing the entire lifecycle of hooks; including installation, editing, configuration, execution, monitoring, debugging, and packaging. Despite the complexity of the framework, our goal is to make the applications simple to create in a reusable way.

Execution model

The overall model for hooks is that each time the hook function is invoked, it is passed data which is managed by the runtime. This data includes points, variables, and configuration data. How frequently the hook is invoked depends on the execution strategy.

The most important piece of data the hook is passed is data from points. The points can be any data from the Object Explorer; for instance, from an underlying hardware system like BACnet or Modbus; or variables from other hooks. Part of the configuration of a hook is a query used to determine which points are passed to the hook.

graph TB
A[Invoke Hook] -->|Invoked due to schedule,<br> API call, or new data| B(Execute Query)
subgraph Track Points
B --> D{Compute Groups}
C[Watch values] -->|The points are watched for<br>new data and metadata| D
end
subgraph "Invoke Hook Groups"
  D --> G1[Group 1]
  D --> G2[Group 2]
  D --> Elipsis[...]
  D --> GN[Group N]
end
G1 --> END[Accumulate Results]
G2 --> END
Elipsis --> END
GN --> END

The hooks themselves are very simple javascript function. Here's an example of a "Hello, World" hook:

const NormalSdk = require("@normalframework/applications-sdk");

/**
 * Invoke hook function
 * @param {NormalSdk.InvokeParams} params
 * @returns {NormalSdk.InvokeResult}
 */
module.exports = async ({points, sdk, config, args}) => {
  console.log("Hello, World!")
}

Invoking a Hook

A hook can be invoked for a few different causes, depending on the use case.

  • By calling StartHook. This allows other applications to invoke the hook.
  • From a Schedule. When a hook is scheduled, the system invokes it every time the schedule period occurs.
  • In response to new time-series data.

Security Model

Since the hooks are general purpose and run within the Normal Framework container, there are several layers of sandboxing applied to make sure the hooks can't interfere with the system or otherwise obtain access to data inappropriately. In particular,

  • Each application runs inside its own chroot, with copies of system libraries, node interpreter, and dependencies not shared with that hook. This prevents app code from seeing any files except for its own.
  • Hooks communicate with Normal only using our published APIs. If API Tokens are enabled, each application is further restricted to the set of scopes defined in its manifest. This can, for instance, ensure that the app has "read only" to data on the site.
  • Resource limits can be applied to each application to prevent a misbehaving hook from compromising the performance of the system.