Skip to Content
New release 12 available 🎉

Go integration

Build a Go application that calls the Babel native licensing runtime through cgo. This guide follows the Go example , from the C compiler flags to the executable’s license checks.

Babel Licensing Native SDK 12.2.0 (C ABI 2) is available for macOS arm64, Linux x64 and Windows x64. The setup script downloads the pinned archive for your platform and verifies its SHA-256 before installation.

Select your platform

The SDK distributes one runtime library for each platform. Botan is embedded; users do not install or ship a Botan or SQLite library. Windows also receives a link-time import library for MSVC/Rust; that file is not another runtime DLL.

Platform packageRuntime fileBuild environment
macOS arm64, macOS 15+.sdk/lib/libBabelLicensingNative.dylibApple Silicon toolchain; Xcode command-line tools for compiled clients
Linux x64, glibc.sdk/lib/libBabelLicensingNative.sox86_64 compiler/toolchain; Ubuntu 22.04 is the initial build baseline
Windows x64.sdk/bin/BabelLicensingNative.dllx64 process; MSVC import library in .sdk/lib/BabelLicensingNative.lib

The installer selects the current OS/architecture, verifies that platform’s archive checksum and metadata, and rejects a different package. Linux checks its minimum glibc version; a musl/Alpine package is not provided. The native runtime uses the OS certificate trust store and system facilities. The release archives are available from the 12.2.0 SDK release .

Linux: install, build and run

Install Python 3.9 or later and the language toolchain. Rust needs Cargo/rustc 1.85 or newer plus a C linker; Go needs Go 1.22 or newer plus GCC/Clang for cgo; C++ needs a C++20 compiler. On Ubuntu, build-essential supplies the C/C++ compiler and linker. Python clients need no C/C++ compiler. The OS needs its normal CA certificate package for HTTPS.

git clone https://github.com/babelfornet/native-licensing-examples.git cd native-licensing-examples python3 scripts/setup-sdk.py python3 scripts/run-example.py go

The shared runner builds the selected client and runs it from the repository root. It does not build the SDK or download dependencies. A successful offline run prints License verified. Feature Pro enabled. The equivalent direct command is:

mkdir -p .build CGO_ENABLED=1 go build -o .build/go-example go/main.go ./.build/go-example

The Rust build script and Go cgo directives set the runtime path on Linux. C++ explicitly supplies -L for link-time lookup and -Wl,-rpath for runtime lookup. Python selects .so in runtime_path(). If you deploy a compiled client elsewhere, ship the single .so in your application layout and configure an appropriate runtime path, such as $ORIGIN/../lib for a sibling lib/ directory.

Windows: install, build and run

Use x64 Python 3.9 or later. For C++ or Rust/MSVC, install Visual Studio’s Desktop development with C++ workload and open an x64 Native Tools terminal. Rust uses the x86_64-pc-windows-msvc toolchain. Go/cgo additionally needs a MinGW-compatible GCC or Clang toolchain, such as MSYS2 UCRT64 or LLVM-MinGW; cl.exe is not Go’s cgo C compiler.

From PowerShell in the example checkout:

python scripts/setup-sdk.py python scripts/run-example.py go

The Python runner makes .sdk/bin available to the child process through its PATH. The C++ runner uses cl when available, Rust links the MSVC import .lib, and Go links the C ABI with its cgo compiler. To build and run directly:

New-Item -ItemType Directory -Force .build | Out-Null $env:PATH = "$PWD\.sdk\bin;$env:PATH" $env:CGO_ENABLED = "1" $env:CC = "C:\msys64\ucrt64\bin\gcc.exe" # or your LLVM-MinGW clang.exe go build -o .build/go-example.exe go/main.go & .\.build\go-example.exe

For deployment, copy BabelLicensingNative.dll next to the executable, or put its directory on the application’s controlled DLL search path. Python loads its explicit absolute DLL path through ctypes.CDLL, using the C calling convention. Do not copy a Linux .so or macOS .dylib onto Windows, and do not distribute the MSVC import .lib as a runtime dependency.

The numbered sections below show the macOS reference build and explain the shared integration code. Offline, activation and floating logic is identical on all three systems; the loader and linker settings above account for the platform differences.

1. Check Go and the C toolchain

The example’s go.mod declares Go 1.22. Use that version or a newer compatible toolchain, with cgo enabled and the Xcode command-line tools installed.

go version go env GOOS GOARCH CGO_ENABLED CC xcode-select -p clang --version python3 --version uname -m

For the supplied native build, the Go target is darwin/arm64. cgo uses a C compiler to produce the bridge between Go and the C ABI. The application does not compile the native library from source and does not require a C++ wrapper package.

Cross-compiling this example requires a native SDK for the destination architecture and a matching C toolchain. The arm64 macOS library cannot be used to build a Windows or Linux executable.

2. Install the example and SDK

git clone https://github.com/babelfornet/native-licensing-examples.git cd native-licensing-examples python3 scripts/setup-sdk.py

Run python3 scripts/setup-sdk.py to download the pinned platform archive. To use a previously downloaded ZIP, add --archive /absolute/path/to/sdk.zip. Both paths verify SHA-256.

The relevant layout is:

native-licensing-examples/ ├── sdk/include/babel/babel_licensing.h ├── .sdk/lib/ │ └── libBabelLicensingNative.dylib ├── demo/ └── go/ ├── go.mod └── main.go

go.mod identifies the sample module; it has no external Go dependencies:

go/go.mod
module github.com/babelfornet/native-licensing-examples/go go 1.22

3. Configure cgo headers and linking

The comment immediately before import "C" is the cgo preamble:

go/main.go
package main /* #cgo CFLAGS: -I${SRCDIR}/../sdk/include #cgo darwin LDFLAGS: -L${SRCDIR}/../.sdk/lib -lBabelLicensingNative -Wl,-rpath,${SRCDIR}/../.sdk/lib #cgo linux LDFLAGS: -L${SRCDIR}/../.sdk/lib -lBabelLicensingNative -Wl,-rpath,${SRCDIR}/../.sdk/lib #cgo windows LDFLAGS: -L${SRCDIR}/../.sdk/lib -L${SRCDIR}/../.sdk/bin -lBabelLicensingNative #include <stdlib.h> #include <babel/babel_licensing.h> */ import "C"

${SRCDIR} is the directory containing main.go, not the shell’s working directory. The paths therefore resolve from go/ back to the repository root.

ConfigurationPurpose
-I.../sdk/includeMakes <babel/babel_licensing.h> available to the C compiler.
-L.../.sdk/libMakes the library available at link time.
-lBabelLicensingNativeLinks the native licensing library. The C compiler adds the platform’s prefix and suffix.
-Wl,-rpath,...Tells the runtime loader where to find the dynamic library after the Go process starts.
<stdlib.h>Declares free, used to release strings allocated by C.CString.

Keep the preamble adjacent to import "C". cgo reads the actual public header and generates the C declarations, so the Go program does not manually duplicate every ABI signature.

The example’s rpath is an absolute development path to this checkout. Rebuild after moving it. A distributable application needs its own installed runtime layout and search path; copying the Go executable alone does not include the SDK.

4. Marshal strings and release them

A Go string is not a NUL-terminated C string. The example converts each input explicitly:

xml := C.CString(text) defer C.free(unsafe.Pointer(xml)) trusted := C.CString(key) defer C.free(unsafe.Pointer(trusted)) algorithm := C.CString(args[2]) defer C.free(unsafe.Pointer(algorithm)) feature := C.CString(args[3]) defer C.free(unsafe.Pointer(feature))

C.CString allocates C memory. The Go garbage collector does not free it; each allocation needs C.free. unsafe.Pointer converts the typed pointer for that deallocator. It does not transfer ownership to Go.

readInput reads at most the allowed size plus one byte. It rejects oversized files and embedded NUL bytes before conversion. The limits are 1 MiB for the license and 16 KiB for the publisher key.

The C library returns a handle allocated by its own allocator. Release that handle with babel_license_free, not with C.free:

var handle *C.babel_license if status := C.babel_license_load(xml, &handle); status != C.BABEL_OK { return fmt.Errorf("license parsing failed (status %d)", status) } defer C.babel_license_free(handle)

&handle is the output slot; load returns a status separately from the pointer. Register the deferred release only after a successful load. The handle points to native memory, rather than a Go object retained by the C library.

5. Apply the validation sequence

The complete source  checks ABI version 2 before loading a file, then performs these checks:

if status := C.babel_license_verify(handle, algorithm, trusted); status != C.BABEL_OK { return fmt.Errorf("publisher signature verification failed (status %d)", status) } if status := C.babel_license_validate(handle); status != C.BABEL_OK { return fmt.Errorf("runtime license validation failed (status %d)", status) } if C.babel_license_feature_enabled(handle, feature) != 1 { return fmt.Errorf("feature %s is absent or expired", args[3]) }

A successful parse only creates an untrusted handle. Verification authenticates the file with the supplied trusted publisher key; runtime validation checks expiry and refuses unsupported restrictions. The feature query also enforces feature expiry.

The example keeps this code inside run(), which returns an error to main(). Its deferred releases run before main() calls os.Exit(1). Calling os.Exit directly inside run() would bypass those defers.

Do not share the native handle with a goroutine that might free it during another call. The example keeps one handle within one validation operation.

6. Build the executable

From the repository root:

mkdir -p .build CGO_ENABLED=1 go build -o .build/go-example go/main.go ./.build/go-example

The result is a compiled Go executable at .build/go-example. It still dynamically loads the native runtime through the configured rpath. You do not need Go or .NET merely to launch that built executable, but the native libraries must remain available.

Expected output:

License verified. Feature Pro enabled.

./run.sh go runs the same build and then starts the executable. The wrapper is convenient, but it is not required for integration.

The default license paths are relative to the process working directory, so run the default example from the repository root. If launching it from another directory, supply absolute license and key paths.

7. Test trial, perpetual and customer files

./.build/go-example demo/trial.xml ./.build/go-example demo/perpetual.xml ./.build/go-example \ /absolute/path/customer.xml /absolute/path/publisher-key.txt RSA Pro ./.build/go-example demo/expired.xml ./.build/go-example demo/no-feature.xml ./.build/go-example demo/restricted.xml

The four positional arguments are the license path, trusted publisher key path, algorithm and feature name. Defaults are the supplied perpetual demo, demo publisher key, RSA and Pro.

The expired file returns status 10; an unsupported restriction returns status 4. Both produce a nonzero process exit status. A missing feature is also refused. Never treat a nonzero native status as an enabled feature.

Use your own pinned key with your own licenses. The separate key file in this example is a configuration aid for testing; production code must obtain that trust anchor from the application or trusted configuration, not from the license document.

For actual trial durations, use the publisher-side issuer. It can create a 30-day absolute-expiry trial using the .NET components from a trial or purchased Babel Licensing SDK. The native client does not need the issuer’s private key.

Troubleshooting

SymptomCheck
Cgo code is excluded or the bridge does not buildCGO_ENABLED=1 and a working C compiler are available.
Header not found#cgo CFLAGS points to the directory above babel/.
Linker cannot find BabelLicensingNative.sdk/lib is present and matches the preamble’s path.
Loader cannot find the libraryCheck the executable’s rpath with otool -l .build/go-example. Keep libBabelLicensingNative.dylib at the configured runtime path.
Architecture mismatchgo env GOARCH, uname -m and file .sdk/lib/libBabelLicensingNative.dylib agree on arm64.
Signature failsThe file and publisher key belong together, and no signed field was edited.

python3 scripts/test-examples.py exercises the accepted and rejected paths in Go and the other three languages.

Run activation and floating licensing

The online modes use the same application and the same single SDK runtime as the offline example. They need a service endpoint, a customer user key and a publisher public key pinned by your application. Do not supply the license used to evaluate or purchase Babel Licensing itself: these are licenses for your own application.

For a self-contained local protocol demonstration, start the fixture in a separate terminal from the repository root:

python3 scripts/demo-service.py

The fixture listens only on 127.0.0.1:8765, reads signed demo payloads, and allocates one disposable floating seat. It needs Python 3, but no .NET runtime, database or signing private key. It is a protocol fixture, not a deployable Licensing Service.

In your application terminal, run:

export BABEL_SERVICE_URL=http://127.0.0.1:8765/ export BABEL_ALLOW_LOOPBACK_HTTP=1 export BABEL_MACHINE_ID=native-example-machine export BABEL_PUBLISHER_KEY_FILE="$PWD/demo/publisher-key.txt" export BABEL_ALGORITHM=RSA export BABEL_CLIENT_ID=native-example-go export BABEL_USER_KEY=demo-activation ./run.sh go --online activation export BABEL_USER_KEY=demo-floating ./run.sh go --online floating

Activation prints a verified grant, validates it again with the service, then deactivates it. Floating prints a verified grant, renews it twice, then releases its seat. Both exit with an error when acquisition, signature verification, machine validation, feature entitlement or a network operation fails.

Use your actual Licensing Service

Configure an activation or floating license for your application in Babel Licensing Service, with a supported RSA or ECDSA P-256 publisher key and a Pro feature (or adapt the feature name). The service must return a valid signed XML or compact license. Stateful trial run counts/days-from-first-use, domain, usage and custom restrictions remain unsupported; an absolute expiry is supported.

unset BABEL_ALLOW_LOOPBACK_HTTP BABEL_MACHINE_ID export BABEL_SERVICE_URL=https://licensing.example.com/ export BABEL_PUBLISHER_KEY_FILE=/absolute/path/your-publisher-public-key.txt export BABEL_ALGORITHM=RSA # Supply BABEL_USER_KEY from your application's secure configuration. ./run.sh go --online activation # Or use a floating user key and --online floating.

Replace the endpoint with your own service root. The SDK appends v1/auth/login and v1/license/*. It authenticates with the user key, then uses a bearer token for service requests. Credentials are read from the environment in these examples and are not printed or passed on the command line. Production applications should use their own secure configuration mechanism.

With BABEL_MACHINE_ID unset, the example calls babel_hardware_key. Keep the machine identity and BABEL_CLIENT_ID stable for an activation; choose distinct client identities for concurrently running floating clients. The fixed machine ID above exists only to match the signed local fixture. Pin the public key in your application or trusted configuration; never trust the key supplied inside a server response.

HTTPS always verifies certificates and hostnames; redirects are not followed. Plain HTTP requires the explicit loopback flag and cannot be used with a remote hostname. The sample timeout is 15 seconds per HTTP request; authentication and the following service request each have their own bound.

Integration code and lifecycle

// C strings, error handling and defers are in runOnline in go/main.go. if status := C.babel_online_activate(client, userKey); status != C.BABEL_OK { return fmt.Errorf("activation failed (status %d)", status) } if status := C.babel_online_check(client); status != C.BABEL_OK { return fmt.Errorf("license unavailable (status %d)", status) }

The existing cgo preamble includes the public C header and links the same single SDK library. The normal go build command above compiles offline, activation and floating paths together. Every C.CString allocation is paired with C.free; the native constructor copies the strings. Cleanup runs before babel_online_free through deferred calls in reverse order.

For activation, runOnline also checks Pro, calls C.babel_online_validate_activation and deactivates. For floating, it requests a seat, reads the signed 64-bit delay from C.babel_online_heartbeat_after_ms, sleeps with time.Sleep(time.Duration(delay) * time.Millisecond), then performs heartbeat and validity checks. Two successful renewals are followed by release. Reject delay < 1 before converting it to time.Duration.

The session API retains the verified license and floating token internally. Use babel_online_check immediately before protected work and babel_online_feature_enabled for feature access. Do not extract or cache a Boolean from the initial acquisition and use it indefinitely.

For a running floating application, schedule heartbeats before the returned deadline throughout the protected session. The two-heartbeat loop is only the executable demonstration. Stop protected work on any heartbeat/check failure. There is no background renewal, offline grace or persistent activation cache in this version. A failed heartbeat invalidates the local grant immediately; a lease cannot be revived after its deadline and must be acquired again.

Release floating seats or deactivate activations explicitly when the application workflow requires it. The examples do this when they exit so repeated evaluations do not consume seats. A product can retain an activation between runs by keeping stable identities and activating/validating it again at startup; this SDK does not save a signed license or token to disk. Freeing a handle releases memory and performs no network request.

If release/deactivation fails, local use is still disabled. Retry cleanup with the same live handle while it exists; a floating token eventually expires at the service. Handle destruction, process termination and network failure do not prove that the server has received cleanup.

Online refusal scenarios

StatusMeaning and application action
2 / 3Malformed response/license or bad publisher signature: refuse protected work.
4Unsupported algorithm, curve or restriction: adjust the issuing template, never bypass the check.
10Signed license expired: obtain a valid application license.
11Network, TLS, timeout or response-size failure: stop protected work and report the failure.
12 / 13 / 14Credentials rejected, HTTP/service failure or grant refused: check service policy and seat availability.
15No active grant, released/expired lease or failed heartbeat: stop use and acquire a new lease as appropriate.
16Signed hardware binding does not match the configured machine: refuse activation.

Run python3 scripts/test-online-examples.py to build/run all four languages against local activation, floating renewal/release, denied grant, hardware mismatch and unsupported-restriction cases. The native SDK separately tests malformed JSON, tampered signed payloads, oversized responses, timeout, redirects, invalid TLS and lease loss. These protocol-fixture checks do not replace acceptance tests against your deployed Licensing Service.

Online commands in Windows PowerShell

The service and user key settings have the same meaning on every OS. For the supplied loopback fixture, open one terminal for python scripts/demo-service.py, then use another:

$env:BABEL_SERVICE_URL = "http://127.0.0.1:8765/" $env:BABEL_ALLOW_LOOPBACK_HTTP = "1" $env:BABEL_MACHINE_ID = "native-example-machine" $env:BABEL_PUBLISHER_KEY_FILE = "$PWD\demo\publisher-key.txt" $env:BABEL_ALGORITHM = "RSA" $env:BABEL_CLIENT_ID = "native-example-go" $env:BABEL_USER_KEY = "demo-activation" python scripts/run-example.py go --online activation $env:BABEL_USER_KEY = "demo-floating" python scripts/run-example.py go --online floating

For your actual HTTPS Licensing Service, remove the loopback flag and fixed demo machine identity, then supply your service root, publisher key and application user key. Run the same --online commands. Linux uses the shell export commands in the online section and can use python3 scripts/run-example.py instead of ./run.sh.

Remove-Item Env:BABEL_ALLOW_LOOPBACK_HTTP -ErrorAction SilentlyContinue Remove-Item Env:BABEL_MACHINE_ID -ErrorAction SilentlyContinue $env:BABEL_SERVICE_URL = "https://your-licensing-service.example/" $env:BABEL_PUBLISHER_KEY_FILE = "C:\YourApplication\publisher-public-key.txt" # Supply BABEL_USER_KEY through your application's secure configuration. python scripts/run-example.py go --online activation

Run python scripts/test-examples.py and python scripts/test-online-examples.py on Windows (or python3 on Linux/macOS) to exercise the same accepted/refused paths in all four bindings.

Last updated on