Package file formatđź”—

Component packages are ordinary zip files with a .vapkg file name extension. A component package can contain multiple components and a license. This makes it possible to distribute a fully functional component with all of its dependencies in a single file.

Structuređź”—

When component packages are created by the Builder, they include exactly one component.

The structure of a component package is, for example, as follows:

com.visionappster.mvp/
com.visionappster.mvp/1/
com.visionappster.mvp/1/component.json
com.visionappster.mvp/1/component.json.crt
com.visionappster.mvp/1/windows-x86_64/plugin.toolplugin
com.visionappster.mvp/1/linux-x86_64/plugin.toolplugin
com.visionappster.mvp/1/macos-x86_64/plugin.toolplugin
com.visionappster.mvp/1/linux-arm/plugin.toolplugin
com.visionappster.mvp/1/Fancy.toolc
com.visionappster.mvp/1/model.onnx

Each component is stored under a path that is composed of the component ID and the major version number of the component. This means that multiple copies of the same component with different major versions can coexists. Different minor or patch versions don’t need to because the requirement is that they are backwards compatible within the major version.

The file system structure of the component itself can be anything, but the example above shows the conventions that should be followed for directory names. The component.json file is a manifest that contains basic information about the component and describes how each file should be interpreted.

General configurationđź”—

Each version directory must contain a file called component.json. The contents of this file are as follows:

{
  "componentId": "com.visionappster.mvp",
  "version": "1.2.3",
  "name": "VisionAppster most valuable product",
  "archs": [
    "windows-x86_64",
    "linux-x86_64",
    "linux-arm_32",
    "macos-x86_64"
  ],
  "files": [
    {
      "arch": "windows-x86_64",
      "filename": "windows-x86_64/plugin.toolplugin",
      "checksum": "21258fdfd376ccce934f9364e55a47242e610101",
      "type": "toolplugin"
    },
    {
      "arch": "linux-x86_64",
      "filename": "linux-x86_64/plugin.toolplugin",
      "checksum": "e5fa44f2b31c1fb553b6021e7360d07d5d91ff5e",
      "type": "toolplugin"
    },
    {
      "arch": "macos-x86_64",
      "filename": "macos-x86_64/plugin.toolplugin",
      "checksum": "7448d8798a4380162d4b56f9b452e2f6f9e24e7a",
      "type": "toolplugin"
    },
    {
      "arch": "linux-arm_32",
      "filename": "linux-arm_32/plugin.toolplugin",
      "checksum": "5d9474c0309b7ca09a182d888f73b37a8fe1362c",
      "type": "toolplugin"
    },
    {
      "filename": "Fancy.toolc",
      "checksum": "a8fdc205a9f19cc1c7507a60c4f01b13d11d7fd0",
      "type": "tool"
    },
    {
      "filename": "model.onnx",
      "checksum": "39e342e32171661a2b44916687b0fc2514fe58bc"
    }
  ],
  "depends": [
    {
      "componentId": "com.l33tcoder.fancycomponent/1",
      "version": ">=1.2"
    },
    {
      "componentId": "system.engine/3"
    }
  ]
}

Note that the checksums are shortened for display. The actual sums are longer.

The file must contain the componentId and version keys. componentId must match the directory name. If a component contains binary plugins such as tool plugins or device drivers, the COMPONENT_ID parameter of the VA_IMPLEMENT_PLUGIN or VA_IMPLEMENT_LICENSED_PLUGIN macro must also match the component ID. The version field contains the exact version number of the component.

The name field is strictly required for the VisionAppster Store only, but giving a human-readable name to a component is good practice.

The archs fields provides a way to list the architectures the component supports. If the component contains no architecture-dependent machine code and therefore runs on any platform, archs can be omitted. The final list of supported architectures is the intersection of the archs lists of all dependent components and the component itself.

If the component requires other components to be functional, those components are listed in the depends array. The componentId field contains the full component ID of the dependency and optionally a version field that specifies the compatible version numbers. Note that because dependencies always contain the major version number, different major versions of a component are always considered incompatible.

Compatible version numbers can be specified either as a string that contains a single rule or an array of such rule strings. If an array of strings is given, all rules must be matched (logical and). Each rule contains an operator and a three-part version number in which trailing zeros may be omitted.

Recognized operators are >, <, >=, <= and =. For example, [">=2.2", "<=2.6"] states that the version number must be between 2.2 and 2.6, inclusive. Special care must be taken with equality and less than checks. Normally they should not be used because upgrades within the same major version are required to be compatible and thus always allowed. They may be useful if a bug is known to break a certain version.

In dependencies, component IDs starting with “system.” refer to system components and let one to specify version requirements for the underlying VisionAppster runtime. In the example above, the component specifies it requires Engine with major version 3.

Finally, the files array lists all files contained in the package. Each must be accompanied with the filename and checksum keys. If type is omitted, the file is treated as a generic resource file with no special meaning.

Checksumsđź”—

All files must be accompanied with a checksum, which is a 64-byte Blake2b hash of the file’s contents, represented as a hexadecimal number with lowercase letters (128 characters in total). The checksum is used to make sure that a licensed file has not been modified after publication. It is also used to ensure the integrity of all other files and for detecting if a file has been changed by the user after installation. This information is useful when uninstalling a component. Loading a licensed file will fail if the checksum of a file doesn’t match.

Signaturesđź”—

When a component package is distributed through the VisionAppster Store, component.json and possible license files must be digitally signed by the Store. The signature is stored in a .crt file alongside the signed file. If an installed component has a signature, its validity will be checked when loading the component. Licensed components distributed through the Store can only be loaded if they have a valid signature.

Copy protectionđź”—

The VisionAppster engine provides protection against unauthorized copying of licensed files. Currently, saved tools (.toolc) and binary plugins (.toolplugin, .datalink) can be protected.

To protect a binary plugin all one needs to do is to use the VA_IMPLEMENT_LICENSED_PLUGIN macro (details here) and pass it the component’s ID and version. This embeds a component ID check to the auto-generated plugin code. The platform verifies the integrity of the plugin binary and the validity of its license.

On the command line, va-pkg tool encrypt can be used to encrypt tool files. This will transform the (about human-readable) .tool files into .toolc files that can only be opened if they have a valid digital signature. Such a signature can be generated using va-pkg sign.

Licensesđź”—

A component package can optionally contain license files and their signatures in a directory called licenses. This makes it possible to craft self-contained packages that don’t require extra licensing steps after installation.

In addition to the license file and its certificate, a component package can contain a configuration file (licenses/config) that contains the information required to fetch a new license file from the Store. The file format is such that it can be easily parsed in any programming language and the shell. Two variables are currently recognized: url specifies where a new license file can be obtained for the user and token is an API token for authorization.

url=https://public-api.visionappster.com/1/licenses/license.vapkg
token=6ee144ad0bbb4bf8aeb4f292febbe5d1

Appsđź”—

The structure of a component package containing a single analysis app is like this:

com.visionappster.awesome.app/1/
com.visionappster.awesome.app/1/component.json
com.visionappster.awesome.app/1/component.json.crt
com.visionappster.awesome.app/1/appconfig.json
com.visionappster.awesome.app/1/main.qml
com.visionappster.awesome.app/1/engine.toolc

The contents of component.json would be as follows:

{
  "componentId": "com.visionappster.awesome.app",
  "version": "1.0.0",
  "name": "So cool app",
  "uuid": "eaec7b02-9ce0-4bbb-acde-50c26be0f35a",
  "files": [
    {
      "filename": "appconfig.json",
      "checksum": "7d97e98f8af710c7e7fe703abc8f639e0ee507c4",
      "type": "app"
    },
    {
      "filename": "main.qml",
      "checksum": "b8bd228ae7f3ff0934cfe36708053db68cabcaf7"
    },
    {
      "filename": "engine.toolc",
      "checksum": "75dffe083d16dee7f91404a1b66d37088da21379"
    }
  ],
  "depends": [
    {
      "componentId": "com.visionappster.mvp/1",
      "version": ">=1.0.0"
    },
    {
      "componentId": "system.engine/2",
      "version": ">=2.9"
    }
  ]
}

The type of appconfig.json is app, which tells the Engine to treat it as an executable application. The appconfig.json contains execution instructions such as a reference to the main component (main.qml).

Although there is a digital signature for the component.json file and a checksum for all of the app’s files, this is only for integrity checking and keeping things consistent, not for enforcing the license of the app’s script code (main.qml). There is no way to identify the script code, which means that nothing prevents users from copying the file and running the app elsewhere. The copied app will however fail if it tries to load the engine.toolc file.

An important thing to note is that the type of engine.toolc is not specified. If it was, the Engine would make com.visionappster.awesome.app/1/engine available as a tool everywhere. This may be wanted sometimes, but in a typical use case the tool is not generally usable and loaded directly from the file by the app’s script code. Therefore, it is considered just another file in the package.

Web appsđź”—

For the VisionAppster engine, web apps are just files that are served through the built-in web server. In the simplest case, the structure of the component package can be for example like this:

com.visionappster.awesome.web/1/
com.visionappster.awesome.web/1/component.json
com.visionappster.awesome.web/1/component.json.crt
com.visionappster.awesome.web/1/index.html

The contents of component.json would be:

{
  "componentId": "com.visionappster.awesome.web",
  "version": "1.0.1",
  "name": "Cooler than ice web app",
  "uuid": "d816b989-8ebe-4701-9847-4d4658233484",
  "files": [
    {
      "filename": "index.html",
      "checksum": "244aa7266b3f5a08321b403b2c59baeba5539b19",
      "type": "webapp"
    }
  ]
}

Here, the type field is optional because any file in the component can be accessed through the Engine’s web server under /components/com.visionappster.awesome.web/1/. The type however makes it possible for the Engine’s front page to know that there actually is something that is intended to be used as a web user interface.

It is possible for a web app to depend on another web app. For example, it is possible to create a library component that only provides JavaScript files other apps depend on. Such library components just don’t have an index.html that would work as the user interface. In other web apps, the files in the library will be accessible using URLs that are relative to the server’s root, for example /components/com.visionappster.weblib/1/library.js.

It is also possible to bundle the web page with all of its resources into a single zip archive. In this case the zip works as if it was a directory. This approach has some performance benefits. By convention, webapp zips should have a .webapp file name extension.

Special file typesđź”—

Saved toolsđź”—

If the type of a file is tool, the file contains an architecture-independent tool saved by the Builder. The base name of the tool file will be used as the name of the tool. It should be noted that although the tool file itself is architecture-independent, it may refer to native tools that are available on certain platforms only.

Binary pluginsđź”—

Binary plugins are compiled machine code and must thus be annotated with an arch field that specifies the target processor architecture and operating system. Three types of binary plugins are supported.

Tool pluginsđź”—

The type of a tool plugin is toolplugin. As a convention, the file name extension of a tool plugin is .toolplugin. The tool C API is used for creating native tool plugins.

Camera driver pluginsđź”—

Camera driver plugins are shared libraries that implement the GenICam GenTL API. To include such a plugin in the component package, the type field of the file must be set to cameradriverplugin. When creating a component package in the Builder, type is set to cameradriverplugin for any files with the extension .gentl or .cti.

Python tool pluginsđź”—

Python tool plugins contain tools implemented in Python. Their type must be pythontoolplugin. By convention, the names of Python tool plugin files follow the pattern *toolplugin.py, for example mytoolplugin.py. Use the Python tool API to create tool plugins in Python.

Resource filesđź”—

A component may require external files to be functional. Typical examples include images and machine learning models. Sometimes, these can be baked into binaries, but this is not always possible or even useful. The type of a resource file is file and can be omitted.

Whenever a component refers to a file in itself or another component at run time, the path is relative to the component installation root directory. The Engine resolves the full path based on the component ID and version number of the referred component.

For example, if a component references res://com.example.foobar/1/model.onnx, the Engine determines where components are installed in the system and inserts the absolute root path to the beginning. resulting e.g. in /opt/visionappster/components/com.example.foobar/1/model.onnx.