Go binds over cgo to the same core library. If shipping a single self-contained binary is the reason you chose Go, link the static library and you still ship one file — the dongle support goes inside it.
d, err := keynub.Open("") // first dongle found
if err != nil { return err }
defer d.Close()
if !d.IsGenuine() { return errors.New("no genuine KeyNub") }
if err := d.SessionOpen(); err != nil { return err }
license, err := d.RecordRead("license")Code language: Go (go)
A note on cgo
cgo is the trade-off here: it means a C toolchain on your build machines and it rules out CGO_ENABLED=0 builds. There is no way around that for a library that talks to USB hardware through the operating system’s HID stack, and it is the same trade-off any hardware-backed licensing makes in Go.
And the rule that matters in every language: do not branch on a boolean. Encrypt data your program genuinely needs with appEncrypt at build time and decrypt it through the dongle at run time, so removing the check leaves the program with nothing to compute rather than a working unlicensed copy.