
Host SDK enabling USB‑C license dongle access via a stable C core and ABI, offering language bindings, samples and driver‑less HID communication for straightforward license management.
Host SDK for the KeyNub USB-C license dongle — language
bindings and samples over one core C library (keynub_licdongle, prefix licd_)
with a stable C ABI. Windows, Linux and macOS, with no driver to install: the
dongle is a vendor-defined USB HID device.
Before you write your licensing check, read
docs/integration-security.md. The dongle proves a genuine device is attached; it cannot stop an attacker patching the application that asks. An integration that branches on a boolean is bypassed trivially — feed something your application needs throughapp_encrypt/app_decryptinstead. That document is short, and it is the difference between real protection and a speed bump.
natives/ —
NATIVES.md says which file is which.The API surface is the same everywhere, because every binding is a thin layer over
the same ABI — declared in include/licdongle.h. Learn it
once.
| Language | Binding | Sample | Package |
|---|---|---|---|
| C |
include/licdongle.h — CMake target keynub::licdongle
|
samples/c |
|
| C++ |
bindings/cpp — header-only RAII, C++11, CMake target keynub::licdongle_cpp
|
samples/cpp |
|
| flat API |
bindings/flat — integer handles, no callbacks |
samples/flat |
— |
| C# / VB.NET / F# |
bindings/dotnet — KeyNub.LicenseDongle
|
samples/csharp, samples/vbnet, samples/fsharp
|
|
| PowerShell |
bindings/powershell — module KeyNub.LicenseDongle, Windows PowerShell 5.1 and PowerShell 7 |
samples/powershell |
|
| Python |
bindings/python — keynub-licdongle, ctypes, plus the licd-tool CLI |
samples/python |
|
| Java |
bindings/java — JNA, Java 17+ |
samples/java |
|
| Clojure |
bindings/clojure — com.keynub/keynub-licdongle-clj, over the Java binding |
samples/clojure |
|
| Delphi / Free Pascal | bindings/delphi |
samples/delphi |
|
| Visual Basic 6 / VBScript |
bindings/com — COM object KeyNub.Dongle
|
samples/vb6 |
— |
| twinBASIC | bindings/com |
samples/twinbasic |
— |
| Excel / VBA | bindings/vba |
samples/vba |
— |
| MATLAB / Simulink |
bindings/matlab — MEX gateway, incl. MATLAB Coder output; runs in GNU Octave |
samples/matlab, samples/simulink
|
|
| Wolfram Language |
bindings/wolfram — paclet over ForeignFunctionLoad, flat API |
samples/wolfram |
|
| LabVIEW |
bindings/labview — VI library (LabVIEW 2026, 64-bit) and the import header |
samples/labview |
|
| Node.js / Electron |
bindings/nodejs — @keynub/licdongle
|
samples/nodejs |
|
| Deno |
bindings/deno — @keynub/licdongle on JSR, Deno.dlopen over the flat API |
samples/deno |
— |
| Go |
bindings/go — cgo, errors.Is sentinels |
samples/go |
|
| Rust |
bindings/rust — keynub-licdongle, no dependencies |
samples/rust |
|
| Ruby |
bindings/ruby — stdlib Fiddle, no gems |
samples/ruby |
|
| PHP |
bindings/php — bundled FFI, no PECL module |
samples/php |
|
| Perl |
bindings/perl — FFI::Platypus
|
samples/perl |
|
| Lua |
bindings/lua — LuaJIT FFI |
samples/lua |
|
| Fortran |
bindings/fortran — F2003 iso_c_binding
|
samples/fortran |
— |
| COBOL |
bindings/cobol — copybook, GnuCOBOL |
samples/cobol |
— |
| Ada |
bindings/ada — Alire crate keynub_licdongle, library loaded at run time |
samples/ada |
|
| Zig |
bindings/zig — @cImport compiles the real header |
samples/zig |
|
| Swift |
bindings/swift — KeyNubLicDongle, SwiftPM package at the repository root |
samples/swift |
|
| Dart / Flutter |
bindings/dart — keynub_licdongle, dart:ffi
|
samples/dart |
|
| Julia |
bindings/julia — ccall, no packages |
samples/julia |
|
| R |
bindings/r — KeyNubLicDongle, a C layer compiled at install |
samples/r |
— |
| Haskell |
bindings/haskell — keynub-licdongle, pure Haskell over the flat API |
samples/haskell |
|
| OCaml |
bindings/ocaml — keynub-licdongle, ctypes-foreign over the flat API |
samples/ocaml |
|
| Elixir |
bindings/elixir — keynub_licdongle, a small NIF over the flat API |
samples/elixir |
|
| Gleam |
bindings/gleam — keynub_licdongle_gleam, typed Gleam over the Elixir package |
samples/gleam |
— |
| D |
bindings/d — keynub-licdongle, extern(C) over the flat API, dub package at the repository root |
samples/d |
|
| Crystal |
bindings/crystal — keynub_licdongle, function pointers over the flat API, shard at the repository root |
samples/crystal |
|
| Tcl |
bindings/tcl — keynub_licdongle, pure Tcl over cffi and the flat API |
samples/tcl |
|
| Raku |
bindings/raku — KeyNub::LicDongle, NativeCall over the flat API |
samples/raku |
— |
| Racket |
bindings/racket — keynub-licdongle, ffi/unsafe over the flat API |
samples/racket |
— |
| Nim |
bindings/nim — importc over dynlib
|
samples/nim |
Every sample carries the exact command that builds and runs it in its header
comment, including which native library it wants. Every binding comes with a
stand-in test that exercises every call of the binding against a stand-in for the
C library, so it runs without a dongle; the binding's README gives the command.
LabVIEW also has a ready-made VI library
(bindings/labview/keynub_licdongle, saved in
LabVIEW 2026, 64-bit); Excel ships a .bas rather than an .xlsm so that the code
can be reviewed in a diff.
Environments that cannot express the core ABI — LabVIEW, VBA, COBOL — go through
a flat companion API (bindings/flat): one self-contained
library with integer handles, caller-allocated buffers and no callbacks.
Visual Basic 6 gets a COM object rather than Declare statements for a specific
reason: VB6's Declare emits stdcall while the flat API is cdecl. That is
harmless in a 64-bit process and a stack-drifting mismatch in a 32-bit one, and
VB6 is 32-bit only. Going through an object removes the question — and adds a
handle that closes itself and failures that raise with a real Err.Description.
tools/licd-tool is one executable with the core built in: list and inspect
dongles, verify them, read and write records, read and increment counters, and encrypt data
that only a dongle can decrypt, from a script or a terminal. Signed Windows builds are in
natives/ (win-x64/licd-tool.exe, win-x86/licd-tool.exe); elsewhere it builds
with CMake against the static library there.
The shortest useful version of docs/integration-security.md:
// Weak — one patched branch defeats it, in any language.
if (dongle.IsGenuine) enableFeature();
// Strong — the data your program needs only exists with the dongle present.
coefficients = dongle.AppDecrypt(blobShippedWithYourInstaller);
Encrypt the constants, tables, thresholds or key material your application cannot compute. Ship them encrypted. Decrypt them through the dongle at run time. Then removing the check does not unlock the feature — it removes the feature's input.
verify_genuine validates the device certificate chain against the KeyNub
production root CA, whose public certificate is compiled into the released library —
so a substituted device fails verification and your application supplies nothing and
manages no root. licd_set_trust_root (or the equivalent on your binding) overrides
the built-in root, which only vendor tooling needs.
The whitepaper behind this SDK covers scope and product boundary, assets, threat model, environment assumptions, security objectives, the mechanisms that meet them, a cryptographic inventory, key management, per-mechanism verification status and the limitations.
Cite it as: AB-Tools GmbH (2026). KeyNub USB-C License Dongle: Security Architecture. Rev. 1.1. Zenodo. https://doi.org/10.5281/zenodo.22860069
Everything in this repository — the bindings, the samples and the C ABI
header — is Apache-2.0. See LICENSE, NOTICE and
THIRD-PARTY-NOTICES.txt for the dependency licence
elections.
The prebuilt native libraries in natives/ are not covered by that
licence; their terms are in BINARY-LICENSE.txt. You can
use them from an Apache-2.0 binding in a closed-source application either way —
that is what they are for.
Security reports: SECURITY.md.
Install packaging/linux/99-keynub-dongle.rules
into /etc/udev/rules.d/ so the device is reachable without root. It is a
permission rule, not a driver — nothing is compiled or loaded into the kernel.
Host SDK for the KeyNub USB-C license dongle — language
bindings and samples over one core C library (keynub_licdongle, prefix licd_)
with a stable C ABI. Windows, Linux and macOS, with no driver to install: the
dongle is a vendor-defined USB HID device.
Before you write your licensing check, read
docs/integration-security.md. The dongle proves a genuine device is attached; it cannot stop an attacker patching the application that asks. An integration that branches on a boolean is bypassed trivially — feed something your application needs throughapp_encrypt/app_decryptinstead. That document is short, and it is the difference between real protection and a speed bump.
natives/ —
NATIVES.md says which file is which.The API surface is the same everywhere, because every binding is a thin layer over
the same ABI — declared in include/licdongle.h. Learn it
once.
| Language | Binding | Sample | Package |
|---|---|---|---|
| C |
include/licdongle.h — CMake target keynub::licdongle
|
samples/c |
|
| C++ |
bindings/cpp — header-only RAII, C++11, CMake target keynub::licdongle_cpp
|
samples/cpp |
|
| flat API |
bindings/flat — integer handles, no callbacks |
samples/flat |
— |
| C# / VB.NET / F# |
bindings/dotnet — KeyNub.LicenseDongle
|
samples/csharp, samples/vbnet, samples/fsharp
|
|
| PowerShell |
bindings/powershell — module KeyNub.LicenseDongle, Windows PowerShell 5.1 and PowerShell 7 |
samples/powershell |
|
| Python |
bindings/python — keynub-licdongle, ctypes, plus the licd-tool CLI |
samples/python |
|
| Java |
bindings/java — JNA, Java 17+ |
samples/java |
|
| Clojure |
bindings/clojure — com.keynub/keynub-licdongle-clj, over the Java binding |
samples/clojure |
|
| Delphi / Free Pascal | bindings/delphi |
samples/delphi |
|
| Visual Basic 6 / VBScript |
bindings/com — COM object KeyNub.Dongle
|
samples/vb6 |
— |
| twinBASIC | bindings/com |
samples/twinbasic |
— |
| Excel / VBA | bindings/vba |
samples/vba |
— |
| MATLAB / Simulink |
bindings/matlab — MEX gateway, incl. MATLAB Coder output; runs in GNU Octave |
samples/matlab, samples/simulink
|
|
| Wolfram Language |
bindings/wolfram — paclet over ForeignFunctionLoad, flat API |
samples/wolfram |
|
| LabVIEW |
bindings/labview — VI library (LabVIEW 2026, 64-bit) and the import header |
samples/labview |
|
| Node.js / Electron |
bindings/nodejs — @keynub/licdongle
|
samples/nodejs |
|
| Deno |
bindings/deno — @keynub/licdongle on JSR, Deno.dlopen over the flat API |
samples/deno |
— |
| Go |
bindings/go — cgo, errors.Is sentinels |
samples/go |
|
| Rust |
bindings/rust — keynub-licdongle, no dependencies |
samples/rust |
|
| Ruby |
bindings/ruby — stdlib Fiddle, no gems |
samples/ruby |
|
| PHP |
bindings/php — bundled FFI, no PECL module |
samples/php |
|
| Perl |
bindings/perl — FFI::Platypus
|
samples/perl |
|
| Lua |
bindings/lua — LuaJIT FFI |
samples/lua |
|
| Fortran |
bindings/fortran — F2003 iso_c_binding
|
samples/fortran |
— |
| COBOL |
bindings/cobol — copybook, GnuCOBOL |
samples/cobol |
— |
| Ada |
bindings/ada — Alire crate keynub_licdongle, library loaded at run time |
samples/ada |
|
| Zig |
bindings/zig — @cImport compiles the real header |
samples/zig |
|
| Swift |
bindings/swift — KeyNubLicDongle, SwiftPM package at the repository root |
samples/swift |
|
| Dart / Flutter |
bindings/dart — keynub_licdongle, dart:ffi
|
samples/dart |
|
| Julia |
bindings/julia — ccall, no packages |
samples/julia |
|
| R |
bindings/r — KeyNubLicDongle, a C layer compiled at install |
samples/r |
— |
| Haskell |
bindings/haskell — keynub-licdongle, pure Haskell over the flat API |
samples/haskell |
|
| OCaml |
bindings/ocaml — keynub-licdongle, ctypes-foreign over the flat API |
samples/ocaml |
|
| Elixir |
bindings/elixir — keynub_licdongle, a small NIF over the flat API |
samples/elixir |
|
| Gleam |
bindings/gleam — keynub_licdongle_gleam, typed Gleam over the Elixir package |
samples/gleam |
— |
| D |
bindings/d — keynub-licdongle, extern(C) over the flat API, dub package at the repository root |
samples/d |
|
| Crystal |
bindings/crystal — keynub_licdongle, function pointers over the flat API, shard at the repository root |
samples/crystal |
|
| Tcl |
bindings/tcl — keynub_licdongle, pure Tcl over cffi and the flat API |
samples/tcl |
|
| Raku |
bindings/raku — KeyNub::LicDongle, NativeCall over the flat API |
samples/raku |
— |
| Racket |
bindings/racket — keynub-licdongle, ffi/unsafe over the flat API |
samples/racket |
— |
| Nim |
bindings/nim — importc over dynlib
|
samples/nim |
Every sample carries the exact command that builds and runs it in its header
comment, including which native library it wants. Every binding comes with a
stand-in test that exercises every call of the binding against a stand-in for the
C library, so it runs without a dongle; the binding's README gives the command.
LabVIEW also has a ready-made VI library
(bindings/labview/keynub_licdongle, saved in
LabVIEW 2026, 64-bit); Excel ships a .bas rather than an .xlsm so that the code
can be reviewed in a diff.
Environments that cannot express the core ABI — LabVIEW, VBA, COBOL — go through
a flat companion API (bindings/flat): one self-contained
library with integer handles, caller-allocated buffers and no callbacks.
Visual Basic 6 gets a COM object rather than Declare statements for a specific
reason: VB6's Declare emits stdcall while the flat API is cdecl. That is
harmless in a 64-bit process and a stack-drifting mismatch in a 32-bit one, and
VB6 is 32-bit only. Going through an object removes the question — and adds a
handle that closes itself and failures that raise with a real Err.Description.
tools/licd-tool is one executable with the core built in: list and inspect
dongles, verify them, read and write records, read and increment counters, and encrypt data
that only a dongle can decrypt, from a script or a terminal. Signed Windows builds are in
natives/ (win-x64/licd-tool.exe, win-x86/licd-tool.exe); elsewhere it builds
with CMake against the static library there.
The shortest useful version of docs/integration-security.md:
// Weak — one patched branch defeats it, in any language.
if (dongle.IsGenuine) enableFeature();
// Strong — the data your program needs only exists with the dongle present.
coefficients = dongle.AppDecrypt(blobShippedWithYourInstaller);
Encrypt the constants, tables, thresholds or key material your application cannot compute. Ship them encrypted. Decrypt them through the dongle at run time. Then removing the check does not unlock the feature — it removes the feature's input.
verify_genuine validates the device certificate chain against the KeyNub
production root CA, whose public certificate is compiled into the released library —
so a substituted device fails verification and your application supplies nothing and
manages no root. licd_set_trust_root (or the equivalent on your binding) overrides
the built-in root, which only vendor tooling needs.
The whitepaper behind this SDK covers scope and product boundary, assets, threat model, environment assumptions, security objectives, the mechanisms that meet them, a cryptographic inventory, key management, per-mechanism verification status and the limitations.
Cite it as: AB-Tools GmbH (2026). KeyNub USB-C License Dongle: Security Architecture. Rev. 1.1. Zenodo. https://doi.org/10.5281/zenodo.22860069
Everything in this repository — the bindings, the samples and the C ABI
header — is Apache-2.0. See LICENSE, NOTICE and
THIRD-PARTY-NOTICES.txt for the dependency licence
elections.
The prebuilt native libraries in natives/ are not covered by that
licence; their terms are in BINARY-LICENSE.txt. You can
use them from an Apache-2.0 binding in a closed-source application either way —
that is what they are for.
Security reports: SECURITY.md.
Install packaging/linux/99-keynub-dongle.rules
into /etc/udev/rules.d/ so the device is reachable without root. It is a
permission rule, not a driver — nothing is compiled or loaded into the kernel.