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.
Data link driver pluginsđź”—
The type
of a data link plugin is datalinkdriverplugin
. As a
convention, the file name extension of a tool plugin is .datalink
.
The data link C API is used for creating native
data link driver 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
.