Skip to content
Start free evaluation

Install and check a license

The PDComm SDK libraries check the license when you start new communication. Without a valid license, or after the evaluation period, they don’t start the new communication and return a dedicated error instead. Communication that is already running keeps working.

A license comes as one text file. Put the file where your application can find it, or install it through the API.

Only the SDKs need a license. A license grants features per module.

Component Module License
SECS/GEM SDK secs-gem; the GEM300 capability catalog uses gem300 Required, bound to host ids
Modbus SDK modbus; Modbus/TCP Security also needs modbus-security Required
OPC UA SDK opcua-client, opcua-server (PubSub included) Required
MQTT mqtt Defined as a license module. You use MQTT through the OPC UA simulator programs, which need no license.
Simulators (GUI, headless, MCP), virtual serial port tools, docs CLI None Not needed. They’re free.

The SECS/GEM simulator (GUI, headless, and MCP) and the Modbus simulator GUI use the SDK libraries, so they embed a tool license that works only for those programs and install it at startup. You don’t install anything for them. In the GUIs, the About window in the Help menu shows the license status and the host id.

Kind (kind) Purpose Validity Host id
evaluation 30-day evaluation 30 days from issue Not bound
developer Developing and distributing applications No expiry; has a maintenance end (updatesUntil) Only the SECS/GEM modules are bound to the host ids of development PCs.
runtime Running SECS/GEM on one piece of equipment As agreed Bound to the host id of the equipment PC
tool Embedded in the PDComm simulators No expiry Not bound; works only for the named programs

An evaluation license grants every module for 30 days. Features, run time, and the number of runs aren’t limited. When the period ends, the state becomes expired and new communication doesn’t start. The days_left field of the status JSON shows the days that remain.

Developer licenses and distribution (OPC UA, Modbus, MQTT)

Section titled “Developer licenses and distribution (OPC UA, Modbus, MQTT)”

Applications you build with a developer license for OPC UA, Modbus, or MQTT are distributed royalty-free. Ship the license text with your application in one of two ways:

  • Have your application install the license text through the API at startup. Compile the text into the executable or ship it as a resource.
  • Put the license file next to your executable under the name pdcomm.license.

The SECS/GEM modules (secs-gem, gem300) are bound to host ids. A developer license lists the host ids of your development PCs, and a runtime license lists the host id of the equipment PC. Evaluation licenses aren’t bound to a host id.

For each piece of equipment:

  1. On the equipment PC, run pdcomm-license host-id or read the host id with the host id API.
  2. Send the host id to receive a runtime license.
  3. Save the file as pdcomm.license next to the application on the equipment PC, or install it another way.

updatesUntil in a developer license is the end of maintenance. If the SDK was built (UTC date) after that date, the state is updates-expired. SDKs built on or before that date keep working. Renew maintenance to move to a newer SDK. The SDK build date is in the output of pdcomm-license version and in build_date of the status JSON.

A host id looks like PDH1-XXXX-XXXX-XXXX-XXXX, where X is an upper-case hexadecimal digit. It’s the first 8 bytes of a SHA-256 hash of the machine id that the operating system records, so the original id can’t be recovered from it.

Operating system Source id How to see it yourself
Linux /etc/machine-id, or /var/lib/dbus/machine-id when that’s missing cat /etc/machine-id
Windows MachineGuid under the registry key HKLM\SOFTWARE\Microsoft\Cryptography reg query HKLM\SOFTWARE\Microsoft\Cryptography /v MachineGuid
macOS The hardware UUID (gethostuuid()) IOPlatformUUID in ioreg -rd1 -c IOPlatformExpertDevice

Read the host id in any of these ways. They all return the same value.

Way Usage
Command pdcomm-license host-id
C (SECS/GEM) pdcomm_license_host_id
C (OPC UA) pdcomm_opcua_license_host_id
C++ pdcomm::License::hostId(), pdcomm::modbus::License::hostId(), pdcomm::opcua::license::host_id()
.NET PDComm.License.HostId(), PDComm.Modbus.License.HostId(), PDComm.OpcUa.License.HostId()
Modbus JSON {"action":"host_id"} returns {"host_id":"PDH1-…"}

The host id changes in these cases. When it changes, get a license reissued for the new host id.

  • You reinstalled the operating system. Linux creates a new machine-id and Windows a new MachineGuid.
  • The logic board of a Mac was replaced.
  • You cloned a virtual machine. Clones can also end up with the same id.

Containers often have no /etc/machine-id. Then the host id can’t be read: pdcomm-license host-id ends with a host id unavailable error, and licenses bound to host ids give host-mismatch. Create a fixed /etc/machine-id in the container, or mount the host’s file read-only.

A license file is a text file with one or more blocks like this one:

-----BEGIN PDCOMM LICENSE-----
eyJ2IjoxLCJpZCI6ImxpY18…(shortened)….Fq9cz…(shortened)
-----END PDCOMM LICENSE-----
  • The contents of a block are signed. If one character changes, the block is rejected.
  • Text outside blocks is ignored, so you can add notes. Both LF and CRLF line endings work.
  • You can put several licenses into one file, for example a developer license and a runtime license in one pdcomm.license.
  • A file can’t be larger than 1 MiB.

The libraries read every one of these locations and combine the licenses they find. A license in one location doesn’t hide licenses in another. When several licenses grant the same module, a license that isn’t an evaluation wins.

Order Location Notes
1 Text installed through the API See Install through the API.
2 Environment variable PDCOMM_LICENSE The license text itself
3 Environment variable PDCOMM_LICENSE_FILE Path of a license file
4 pdcomm.license next to the executable, or else next to the SDK library For distributing with your application
5 pdcomm.license in the user configuration folder See the table below.
6 pdcomm.license in the system configuration folder See the table below.
Operating system User configuration folder System configuration folder
Linux $XDG_CONFIG_HOME/pdcomm/, or ~/.config/pdcomm/ when the variable isn’t set /etc/pdcomm/
macOS ~/Library/Application Support/PDComm/, or else $XDG_CONFIG_HOME/pdcomm/ or ~/.config/pdcomm/ /etc/pdcomm/
Windows %APPDATA%\PDComm\ %ProgramData%\PDComm\
  • In each location only the first file found is used. For example, when there’s a file next to the executable, the folder of the library isn’t checked.
  • An environment variable with an empty value counts as unset.
  • The libraries read the files again on every check. When you replace a file, the next new communication uses it without a restart.
  • When the file that PDCOMM_LICENSE_FILE names can’t be read, the error isn’t ignored: it appears in sources of the status JSON.
터미널 창
# Applies only to programs started from this shell.
export PDCOMM_LICENSE_FILE="$HOME/pdcomm/pdcomm.license"
# Applies to every program of this user (Linux).
mkdir -p ~/.config/pdcomm
cp pdcomm.license ~/.config/pdcomm/pdcomm.license

Text you install through the API applies only to the library whose API you called. If one process uses the SECS/GEM, Modbus, and OPC UA libraries together, install the license in each. Environment variables and files are read by every library.

Language SECS/GEM Modbus OPC UA
C pdcomm_license_install JSON action license_install pdcomm_opcua_license_install
C++ pdcomm::License::install (pdcomm_license.hpp) pdcomm::modbus::License::install (pdcomm/modbus/pdcomm_modbus_license.hpp) pdcomm::opcua::license::install (pdcomm/opcua/pdcomm_opcua.hpp)
.NET PDComm.License.Install PDComm.Modbus.License.Install PDComm.OpcUa.License.Install
  • Installing again replaces the text installed earlier through the API. Installing empty text removes it.
  • Text without any block whose signature verifies is refused, and the text installed earlier stays. C (SECS/GEM) returns PDCOMM_INVALID_DATA, and Modbus returns the error code license_invalid. Text larger than 1 MiB is an invalid argument.
  • An expired license is still installed when its signature verifies. Check whether it’s valid with the status, not with the install result.
/* SECS/GEM: install once before you start communication. */
static const char license_text[] = "-----BEGIN PDCOMM LICENSE-----\n…\n-----END PDCOMM LICENSE-----\n";
PdcommStatus status = pdcomm_license_install((const uint8_t *)license_text, sizeof license_text - 1);
if (status != PDCOMM_OK) {
/* PDCOMM_INVALID_DATA: no license with a valid signature. */
}
// .NET (SECS/GEM): install the license file shipped with the application.
PDComm.License.Install(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "app.license")));
{"action":"license_install","text":"-----BEGIN PDCOMM LICENSE-----\n…\n-----END PDCOMM LICENSE-----\n"}
{"ok":true,"result":{"installed":1}}

installed is the number of blocks whose signature verifies.

Checked calls and what happens when they’re refused

Section titled “Checked calls and what happens when they’re refused”

The license is checked only by calls that start new communication. Arguments are checked first, so invalid arguments return an argument error regardless of the license.

Product Checked calls Module When refused
SECS/GEM pdcomm_tcp_create, pdcomm_tcp_start, pdcomm_secs1_create, pdcomm_secs1_start secs-gem C returns PDCOMM_LICENSE_REQUIRED (16), C++ throws pdcomm::Error with status 16 (its what() starts with PDComm license required), and .NET throws PdcommException whose Status is LicenseRequired.
SECS/GEM pdcomm_gem300_capability_catalog_create, pdcomm_gem300_capability_catalog_replace gem300 Same as above
Modbus JSON action connect modbus, plus modbus-security when the configuration has security PDCOMM_MODBUS_SERVICE_ERROR (5) with the error code license_required
OPC UA pdcomm_opcua_discover, pdcomm_opcua_client_connect opcua-client Status 11 with an error text
OPC UA pdcomm_opcua_server_create opcua-server Same as above

Endpoints you already created, established connections, and running servers keep working when the license expires or changes. The license is checked again when you stop and restart an endpoint or connect again.

The refusal message looks like this. The state is in parentheses.

PDComm license required for module 'secs-gem' (missing): no valid license for this module was found. Existing connections keep running. Install a license with the license API, PDCOMM_LICENSE_FILE or a pdcomm.license file.

Modbus puts a code and the license state into the error reply:

{"code":"license_required","error":"PDComm license required for module 'modbus' (expired): license lic_… has expired. …","license":{"license_id":"lic_…","module":"modbus","state":"expired"},"ok":false}

The SECS/GEM C ABI has no last-error text. When you get status 16, use the status API to see why.

Language SECS/GEM Modbus OPC UA
C pdcomm_license_status JSON action license_status pdcomm_opcua_license_status
C++ pdcomm::License::status() pdcomm::modbus::License::status() pdcomm::opcua::license::status()
.NET PDComm.License.Status() PDComm.Modbus.License.Status() PDComm.OpcUa.License.Status()

The C functions copy JSON text into the caller’s buffer. When the buffer is too small, they leave it unchanged and report the size they need. .NET returns a JsonElement. The main fields of the status JSON are:

Field Contents
build_date Build date of this SDK (UTC)
host_id, host_id_error The host id of this computer; null when it can’t be read, with the reason in host_id_error
program Executable name (lower case, without extension)
keys Signing keys this SDK verifies
sources Locations found (source, location), block count (blocks), and read errors (error)
licenses The contents and verification result of each block (verification: valid, malformed, unknown-key, or invalid-signature)
modules For each module: state, granted, days_left, license_id, kind, expires_at, rejected, and host_bound

source in sources is one of api, env:PDCOMM_LICENSE, env:PDCOMM_LICENSE_FILE, executable-dir, library-dir, user-config, and system-config.

The SDK packages contain the pdcomm-license command for checking licenses.

Command What it does
pdcomm-license host-id Prints the host id of this computer.
pdcomm-license inspect <file> [--json] Shows the signature check and the contents of each block in the file.
pdcomm-license verify <file> [--module <module>]... [--program <name>] [--json] Shows the state of each module for this computer, the current time, and this SDK build.
pdcomm-license status [--json] Shows the state from the licenses found in environment variables and configuration folders.
pdcomm-license version Prints the tool version, the build date, and the keys it verifies.

The exit status is 0 for success, 1 when not granted or no valid license was found, and 2 for usage or file errors. With --module, verify exits with 0 when every named module is granted; without it, when any module is granted.

$ pdcomm-license verify pdcomm.license --module secs-gem --module modbus
secs-gem: evaluation (25 days left) [lic_…]
modbus: evaluation (25 days left) [lic_…]

status shows the view of pdcomm-license itself. It sees the same environment variables and configuration folders as your application, but not text your application installed through the API or files next to your application’s executable. Use the status API to see what your application sees.

State Meaning What to do
missing No valid license grants the module. Check that the license file appears in sources of the status JSON and that the module is in the license’s modules. If rejected is set, see the rejection table below.
expired The license expired (expiresAt). An evaluation reaches this state after 30 days. Get a developer license or a new license.
not-yet-valid The license isn’t valid yet (notBefore). Check the clock and time zone of the computer.
updates-expired The SDK was built after the maintenance end (updatesUntil). Use an SDK built before the maintenance end, or renew maintenance.
program-mismatch A tool license doesn’t cover this program. Tool licenses are only for the PDComm simulators. Use an evaluation or developer license for your application.
host-mismatch A license bound to host ids (SECS/GEM developer or runtime) doesn’t list this computer. Send the output of pdcomm-license host-id to get the license reissued. If the host id can’t be read, see Host ids.

A rejected block grants nothing. The state is then missing, and the reason appears in rejected under modules and in verification under licenses.

Rejection Meaning What to do
malformed The block can’t be parsed: characters were lost while copying, or the format isn’t supported. Copy the file you received again, as is. Copy the whole block, including the first and last lines.
unknown-key The block is signed with a key this SDK doesn’t know. A test license was used with a released SDK, or the license was issued with a newer key. Get the license reissued or update the SDK.
invalid-signature The signature doesn’t match: the contents of the block changed. Make sure nobody edited the block, and use the original file again.

To find out which location a license came from, set PDCOMM_LICENSE_NO_SEARCH=1. The libraries then read only the text installed through the API, PDCOMM_LICENSE, and PDCOMM_LICENSE_FILE, and don’t search the file locations (4 to 6).

터미널 창
PDCOMM_LICENSE_NO_SEARCH=1 PDCOMM_LICENSE_FILE=./pdcomm.license pdcomm-license status