DatumHueEnterprise v0.0.24datumhue.com
Contents

Native plugins

Add whole new namespaces and handle classes to the datumhue.* API with a native plugin: a library of native code the workstation loads. Your app can then drive a vendor's SDK, an in-house system, a piece of hardware or a store platform that DatumHue does not ship.

A plugin runs as native code, with the same authority as the workstation itself, outside the Lua sandbox. Your app never gains that authority by calling one: it calls a plugin only when it is granted the plugin, as it is granted a folder or a permission. Whoever runs the workstation decides which plugins exist at all, by installing them.

The Native plugin ABI reference lists the symbols a plugin exports, the value encoding and the catalog schema.

First question: new verbs, or new data?

Before you write a plugin, decide what you are adding.

If you are reading a proprietary file format, such as a vendor's container or a bespoke dataset, you usually want new data, not new verbs. A native data mount gives you that. It returns the same DataHandle values the built-in loaders return, so queries and charts work on it unchanged, and it adds nothing to the Lua API. It is cheaper to write and cheaper to trust, so reach for it first.

Write a plugin when you need new verbs: operations that are not "load these rows", such as unlocking an achievement, querying a device, or calling a vendor SDK whose surface is a set of actions. Then you want new functions and new handle classes on datumhue.*, and a plugin is the tool.

How a plugin extends the API

A plugin declares its API as a catalog. Before any app runs, the workstation checks the catalog and adds it to datumhue.*, so a plugin's functions are typed, documented and granted exactly like the built-in ones.

An installed plugin's namespaces appear in this manual's reference (datumhue docs) and in the datumhue.d.lua that datumhue pkg refresh writes for your editor. Both read the folder the DATUMHUE_PLUGINS_DIR environment variable names, without starting any plugin. Set the variable, not --plugins-dir, which sets the folder for one launch only. Completion and type checks then cover your plugins with no extra step.

A plugin adds three kinds of thing:

  • Namespaces, mounted as datumhue.<name> beside datumhue.draw and the rest, holding functions and properties.
  • Handle classes: typed values with their own methods and properties, shaped like the built-in handles such as Image or DataHandle. You hold them and pass them back to the plugin's functions, as you would any other handle.
  • Methods on Bytes: decoders that read a format the way Bytes:text and Bytes:csv do. A decoder that yields a table returns a DataHandle, the same as a built-in loader. Bytes is the one class a plugin may extend. A name the core class or another plugin already uses refuses the whole library at load, so start each name with your vendor's prefix, as in Bytes:acme_table.

Two conventions complete the contract. An asynchronous plugin function that can fail accepts the call and reports the failure through its completion; the caller's pcall then receives the full message. Refusing the call at the start reaches pcall only as a generic failure. And a handle that holds a native resource, such as a session or an open file, has an explicit close method: garbage collection never reaches into a plugin, so nothing is released for you.

Reaching a plugin from a package

A plugin the workstation has loaded is not callable until a package asks for it. Declare it in package.kdl, by name and by a range of the plugin's own versions:

kdl
requires_plugin "acme_formats" "^1.2"

A plugin's version is its own, separate from the DatumHue API version. When your app starts, DatumHue checks that a plugin of that name is loaded at a version in the range. If it is missing or too old, the app does not start, and the error names the plugin.

Otherwise the app is granted "plugin:acme_formats". That grant follows the rules every grant does: it flows down to the apps yours starts, and only narrows (Permissions and capabilities). A call into a plugin your app was not granted raises.

A package that works without the plugin adds optional=#true to the line. The app then starts either way, and while the plugin is missing its calls raise, so make the first call through pcall to find out whether it is there. The manifest reference has every form of the key.

Inside your app, a plugin's API reads like any other:

lua
local acme = datumhue.acme_formats
local data = datumhue.args.files.report:read():ready()

-- A namespace function, returning the plugin's own handle.
local doc = acme.open(data)
print(doc.page_count, doc:page_text(1))
doc:close()

-- A decoder the plugin added to Bytes.
local sales = data:acme_table()
print(sales:count())
  • acme_formats.open takes the report's bytes and returns an AcmeDoc, a handle class the plugin declares, with a page_count property and methods of its own.
  • AcmeDoc:close releases what the plugin holds for the document.
  • Bytes:acme_table decodes the same bytes into a DataHandle, which queries and charts take like any other table.

The shape of a plugin

A plugin is a shared library: a .so on Linux, a .dll on Windows, a .dylib on macOS. It speaks a fixed contract with the workstation, and four things about that contract shape how you call a plugin:

  • A function returns at once or suspends. A synchronous function returns its result on the spot. An asynchronous one is a suspending call: it pauses your app until the plugin posts its result, then returns it. You write no callback.
  • Handles are typed. A result that is not a plain value comes back as a handle of one of the plugin's classes, which compares and prints like a built-in handle.
  • Errors raise. A plugin's own failure raises a Lua error, like every other datumhue.* call. Wrap a call in pcall to handle the failure instead of passing it on.
  • Values cross as data. Arguments and results are copied across, encoded as MessagePack. A plugin never touches your app's Lua state.

Writing one

The steps, at a glance; the Native plugin ABI reference holds the exact contract:

  1. Build a shared library that exports the entry points the reference lists under exported symbols, for the ABI generation it names. In Rust, that is a cdylib crate. The library returns its catalog as data, implements each function it declares, and posts the results of its asynchronous ones.
  2. Add a sidecar if you want one. A <stem>.kdl file beside the library can pin the ABI generation the library expects (abi) and its content hash (hash). It takes the library's file stem: acme.so reads acme.kdl. The workstation then refuses a library that does not match, which catches a swapped or damaged file.
  3. Install it in the plugin folder. The workstation loads plugins from the folder --plugins-dir or DATUMHUE_PLUGINS_DIR names. Every library in it built for the right ABI generation loads, with no signature check, so protect the folder as you protect the workstation's own files.

The hard rules

Two rules are part of the contract, not style. Breaking either crashes the workstation:

  • No panic may cross the boundary. No Lua error, Rust panic or foreign exception may unwind out of a plugin's entry point. Build the library with panic = "abort", or wrap each export so it catches a failure and returns it as an encoded error. An error is a value you return, never an unwind.
  • A crash takes the workstation down. A plugin runs inside the workstation's own process, in the same memory, with no sandbox around it. If it faults or aborts, the whole workstation goes with it, so hold a plugin to the standard you hold the workstation to.

See also

Reference used on this page: datumhue.args File:read Bytes:ready datumhue.draw Bytes:text Bytes:csv

datumhue.com

(c) 2026 Shuusei AB. Quote excerpts of this manual, with attribution to DatumHue, in your own documentation, tutorials, course materials, reviews, and other writing or video. Republishing it in whole or in substantial part needs written permission: support@datumhue.com.

Victor Mono is licensed under the SIL Open Font License 1.1
Copyright (c) 2024, Rune Bjørnerås (https://github.com/rubjo)

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL


-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Try

    ↑↓ selectEnter openTab filter

    Settings

    Theme
    Text
    Print

    Keyboard shortcuts

    / or Ctrl KSearch
    ↑ ↓ EnterPick a result
    TabNext result filter
    j kNext or previous entry
    [ ]Previous or next page
    yCopy a link to the current entry
    ?These shortcuts
    EscClose

    On this page