# KeyNub License Dongle — Haskell package
```haskell
import KeyNub.LicDongle
main :: IO ()
main = withDongle open $ \d -> do -- first dongle, or openSerial "..."
_ <- verifyGenuine d -- throws unless genuine
secret <- withSession d $ -- closed on every exit path
appDecrypt d sealed -- <- build the licence check on this
...
```
**Pure Haskell.** The package calls the SDK's flat companion API through
function pointers resolved at run time, so nothing is linked at build time,
there is no C to compile and nothing sits in the path of the check that a
customer could substitute. `base`, `bytestring`, `directory`, `filepath` and
the platform's loader package (`unix`, or `Win32` on Windows) are the
dependencies. GHC 9.4 or later, on Windows, Linux and macOS.
## Setup
```
cabal install keynub-licdongle
```
or `keynub-licdongle` in `build-depends`. The package does not carry the
native library. Take `keynub_licdongle_flat` for your platform from the SDK's
[natives folder](https://github.com/AB-KeyNub/KeyNub-SDK/blob/master/NATIVES.md)
and either put it where the operating system finds libraries (next to the
executable, or on `PATH`, `LD_LIBRARY_PATH`, `DYLD_LIBRARY_PATH`), or name it
before the first call:
```haskell
setLibraryPath "/opt/keynub/libkeynub_licdongle_flat.so"
```
`KEYNUB_LICDONGLE_FLAT_LIBRARY` in the environment does the same. In a clone
of the SDK repository the package finds `natives/<platform>/` on its own, from
the working directory upwards, so the samples run with nothing set. A process
loads the library once; `loadedLibraryPath` tells which. On Linux, install the
udev rule described in
[`NATIVES.md`](https://github.com/AB-KeyNub/KeyNub-SDK/blob/master/NATIVES.md)
so the dongle is accessible without root.
## Notes
- Results are records (`Info`, `GenuineResult`, `Device`, `RecordInfo`); byte
data is `ByteString`. The package calls the SDK's flat API: integer handles
and caller-provided buffers, no C structures and no hand-written layouts.
- Failures throw `LicDongleError` with `errorStatus` (`NoDevice`,
`NotGenuine`, `AuthRequired`, ...), `errorCode`, `errorOperation` and
`errorDetail`; loading problems throw `LibraryError`.
- `isGenuine` is the non-throwing form for a gate and **fails closed**: every
failure gives `False`.
- `withDongle` and `withSession` are `bracket`s: the dongle and the session
are closed on every exit path, exceptions included.
- `eraseAllRecords` is deliberately separate from `eraseRecord`: an
accidentally empty name must not wipe the dongle.
- Records are transferred in one call; the flat API has no progress
reporting.
> Read [`docs/integration-security.md`](https://github.com/AB-KeyNub/KeyNub-SDK/blob/master/docs/integration-security.md)
> before writing the check. `ok <- isGenuine d; unless ok exitFailure` is one
> conditional branch, and patching one of those in a release binary is a
> beginner exercise. Route something the program needs through `appEncrypt` and
> `appDecrypt`, so removing the check removes the data.
## Tests
`cabal test` runs without a dongle: it compiles a stand-in for the flat C API
(`bindings/flat/licd_flat.c` over `bindings/julia/test/stub/licd_stub.c`) with
the C compiler on the path and exercises every call against it. In a package
taken from Hackage the SDK sources are not present; point `KEYNUB_SDK_ROOT` at
a clone, or `KEYNUB_LICDONGLE_FLAT_LIBRARY` at a compiled stand-in.
## Links
- [KeyNub License Dongle for Haskell](https://www.keynub.com/developers/haskell/): the product, and how to
order one
- [Source, samples and issue tracker](https://github.com/AB-KeyNub/KeyNub-SDK) on GitHub
- [Native library for your platform](https://github.com/AB-KeyNub/KeyNub-SDK/blob/master/NATIVES.md)