godp is an enterprise-grade, high-performance server-side biometric processing microservice written in Go, wrapping the official DigitalPersona / Crossmatch U.are.U C SDK (libdpfj).
Designed specifically for headless server deployments, godp receives raw fingerprint scans or candidate templates from client workstations (Windows, Linux, Web, or Mobile) and handles multi-sample enrollment, ISO/ANSI minutiae extraction, 1:1 verification, and 1:N biometric identification at cloud scale.
- Key Capabilities
- Quick Start (Local Development)
- SDK Library Installation
- Deployment Options
- Configuration Reference
- API Reference Summary
- License
- Server-Side Enrollment Engine: Fuses 2 or more raw captures (3–4 recommended) of the same finger into one DigitalPersona registration template (
DP_REG_FEATURES) usingdpfj_start_enrollment/dpfj_add_to_enrollment/dpfj_create_enrollment_fmd. Returns a JPEG preview of the last capture alongside the template. - Bundled SDK, No Separate Installer: Pre-compiled DigitalPersona libraries (
lib/x64) and C headers (lib/include) ship inside the repository. The built binary locates them through an embedded$ORIGINruntime path, so no manualLD_LIBRARY_PATHis needed as long as thelib/x64directory stays next to the binary layout described in SDK Library Installation. - Standard Compliant: Native support for international biometric interchange formats:
- ISO/IEC 19794-2:2005
- ANSI INCITS 378-2004
- High-Throughput Verification (1:1 & 1:N): Sub-millisecond matching using hardware-accelerated minutiae comparators with configurable False Accept Rate (FAR) thresholds.
- NIST NFIQ Quality Assessment: Evaluates fingerprint clarity (score 1 to 5) before enrolling.
- Production-Grade Microservice:
- Built-in graceful shutdown (
SIGINT/SIGTERM) - Structured JSON logging (
log/slog) - Configurable request size limiting (
MaxBytesReader) - Optional API Key authentication (
X-API-KeyorBearer) - CORS pre-flight handling
- Built-in graceful shutdown (
Prerequisites: Linux x86_64, GCC, Go 1.22+. The DigitalPersona SDK libraries are already included in lib/x64; there is nothing extra to install for local development.
# Clone repository and enter directory
git clone https://github.com/banguncode/godp.git
cd godp
# 1. Run automated test suite with race detector
make test
# 2. Build and run server (default port 5050)
make runTest the health probe:
curl -s http://localhost:5050/health | python3 -m json.toolIf make test or make run fails with error while loading shared libraries, see Troubleshooting.
godp depends on the native DigitalPersona U.are.U shared libraries (libdpfj.so, libdpfpdd.so.3, and related files). They are bundled in lib/x64, so you normally do not need to install the SDK separately. This section explains how they are found at runtime and what to do when they are not.
The Linux dynamic loader must be able to find libdpfj.so and its dependencies when the process starts. godp supports three ways, in order of preference:
| Method | Used by | Notes |
|---|---|---|
Embedded $ORIGIN rpath |
make build / make run binary, systemd |
Resolved relative to the binary's own location. No configuration needed. |
Absolute rpath + LD_LIBRARY_PATH |
make test |
go test builds its binary in a temporary directory (/tmp/go-build.../), so $ORIGIN does not point at lib/x64. The Makefile passes an absolute path instead. |
System-wide (ldconfig) |
Any process on the host | Optional. See Option 2. |
Run these from the repository root to confirm the bundled SDK is complete:
# The required libraries should be present
ls -l lib/x64/ | grep -E 'dpfj|dpfpdd'
# No line should say "not found"
ldd lib/x64/libdpfj.so | grep -i "not found"
# After building, check the binary resolves everything
make build
ldd bin/godp-server | grep -Ei "dpfj|dpfpdd|not found"Expected: libdpfpdd.so.3 -> libdpfpdd.so.3.0.0 is present in lib/x64, and ldd reports no not found entries.
Keep symlinks intact. The
.sofiles are versioned symlinks (for examplelibdpfpdd.so -> libdpfpdd.so.3 -> libdpfpdd.so.3.0.0). When copying, usecp -a(orcp -r) rather than copying single files, otherwise the.so.3name the loader asks for will be missing.
Nothing to install. Keep the directory layout so $ORIGIN resolves correctly:
godp/
├── bin/godp-server # binary built by `make build`
└── lib/x64/ # DigitalPersona libraries
The same layout is used by the systemd deployment:
/opt/godp/
├── bin/godp-server
└── lib/x64/
Both make install-systemd and the manual steps below produce this layout.
Use this when other applications on the server (not only godp) must load the SDK, or when a binary lives in a directory without a sibling lib/x64. Once registered with ldconfig, the libraries are found by every process without LD_LIBRARY_PATH or rpath.
Step 0: Check for leftover copies. An older manual install (for example in /lib or /usr/lib) can conflict with this one. The output should be empty or say "clean":
ldconfig -p | grep -E 'dpfj|dpfpdd' || echo "clean"If old entries show up, find out where they came from before removing anything:
ls -l /lib/libdpf* /usr/lib/libdpf* 2>/dev/null
dpkg -S /lib/libdpfj.so.3 2>&1If dpkg -S reports no path found matching pattern, the files were copied by hand and can be removed:
sudo rm -f /lib/libdpfj.so* /usr/lib/libdpfj.so* /usr/lib/libdpfpdd*
sudo ldconfigIf dpkg -S names a package, remove it with sudo apt remove <package> instead.
Step 1: Install. Run from the repository root:
# Copy libraries and headers, preserving symlinks
sudo mkdir -p /usr/local/lib/godp /usr/local/include/godp
sudo cp -a lib/x64/. /usr/local/lib/godp/
sudo cp -a lib/include/. /usr/local/include/godp/
# Register the library directory with the dynamic loader
echo "/usr/local/lib/godp" | sudo tee /etc/ld.so.conf.d/godp.conf
# Let every user (www-data, service accounts, ...) read the files
sudo chmod -R a+rX /usr/local/lib/godp /usr/local/include/godp
# Refresh the loader cache
sudo ldconfigStep 2: Verify. First confirm the loader knows the libraries:
ldconfig -p | grep -E 'dpfj|dpfpdd'Every entry should point to /usr/local/lib/godp/.... Then prove that a binary outside the repository can use them. A plain ldd ~/godp/bin/godp-server is not enough, because it still resolves through the $ORIGIN rpath to lib/x64 first:
mkdir -p /tmp/uji/bin
cp ~/godp/bin/godp-server /tmp/uji/bin/
cd /tmp
env -u LD_LIBRARY_PATH ldd /tmp/uji/bin/godp-server | grep -Ei "dpfj|dpfpdd|not found"
rm -rf /tmp/ujiExpected: libdpfj.so.3 and libdpfpdd.so.3 resolve to /usr/local/lib/godp/... and nothing says not found.
Step 3: Use it from other applications.
| Application type | What to do |
|---|---|
| Prebuilt binaries and systemd services | Nothing. The loader finds the libraries through the ldconfig cache. |
| C / C++ | gcc app.c -I/usr/local/include/godp -L/usr/local/lib/godp -ldpfj |
| Go (cgo) | CGO_CFLAGS="-I/usr/local/include/godp" CGO_LDFLAGS="-L/usr/local/lib/godp -ldpfj" go build |
Python (ctypes), PHP (FFI), etc. |
Load by soname, no path needed: ctypes.CDLL("libdpfj.so.3") |
| Docker containers | Not automatic, containers are isolated from the host. Copy the libraries into the image, or mount them: -v /usr/local/lib/godp:/usr/local/lib/godp:ro and run ldconfig inside the container (or set LD_LIBRARY_PATH=/usr/local/lib/godp). |
Linking with
-ldpfjneeds the unversioned symlinklibdpfj.so. Check withls -l /usr/local/lib/godp/libdpfj*. If onlylibdpfj.so.3exists, create it:sudo ln -s libdpfj.so.3 /usr/local/lib/godp/libdpfj.so && sudo ldconfig.
Updating the SDK. After replacing files in lib/x64, repeat Step 1 (the cp -a lines and sudo ldconfig). Existing files are overwritten and the cache is refreshed.
Uninstalling.
sudo rm -f /etc/ld.so.conf.d/godp.conf
sudo rm -rf /usr/local/lib/godp /usr/local/include/godp
sudo ldconfig
# Verify: nothing should be registered anymore
ldconfig -p | grep -E 'dpfj|dpfpdd' || echo "clean"This does not touch lib/x64 inside the repository or /opt/godp (systemd deployment).
Systemd. make install-systemd copies lib/x64 to /opt/godp/lib/x64, next to /opt/godp/bin/godp-server, so the $ORIGIN rpath works as-is. If the service fails with cannot open shared object file, add this line to the [Service] section of /etc/systemd/system/godp.service, then run sudo systemctl daemon-reload && sudo systemctl restart godp.service:
Environment=LD_LIBRARY_PATH=/opt/godp/lib/x64Docker. The provided Dockerfile copies the bundled libraries into the image, so no SDK needs to be installed on the host. If you write your own Dockerfile, make sure the final stage includes the libraries and points the loader at them:
COPY lib/x64 /opt/godp/lib/x64
COPY --from=build /src/bin/godp-server /opt/godp/bin/godp-server
ENV LD_LIBRARY_PATH=/opt/godp/lib/x64| Symptom | Cause | Fix |
|---|---|---|
libdpfpdd.so.3: cannot open shared object file when running go test directly |
Test binary is built in /tmp, so $ORIGIN does not resolve to lib/x64 |
Use make test, or run LD_LIBRARY_PATH=$(pwd)/lib/x64 go test -v -race ./pkg/dp/... |
Same error when running bin/godp-server |
Binary was moved away from its lib/x64 sibling directory |
Restore the layout from Option 1, or use Option 2 |
| Same error under systemd | Libraries missing from /opt/godp/lib/x64, or wrong ownership/permissions |
Re-run make install-systemd, or set Environment=LD_LIBRARY_PATH=/opt/godp/lib/x64 |
ldd shows libxxx => not found for a library that is not in lib/x64 |
A system package is missing (for example libstdc++, libusb) |
Install it with apt install <package> and re-check with ldd |
.so.3 missing but .so.3.0.0 exists |
Symlinks were lost when copying | Re-copy with cp -a, or recreate: ln -s libdpfpdd.so.3.0.0 libdpfpdd.so.3 |
ldconfig -p lists dpfj/dpfpdd under /lib or /usr/lib |
Leftover manual copy from an earlier install, may shadow or duplicate the new one | Follow Step 0 in Option 2 |
Works as your user, fails as www-data or a service user |
Library files are not world-readable | sudo chmod -R a+rX /usr/local/lib/godp && sudo ldconfig |
| Works on the host, fails inside a container | Containers do not see the host's ldconfig cache |
Copy the libraries into the image or mount /usr/local/lib/godp |
wrong ELF class or cannot execute binary file |
Architecture mismatch (not x86_64) | The bundled libraries are Linux x86_64 only |
Useful diagnostics:
# See exactly which libraries a binary resolves
ldd bin/godp-server
# See which paths the loader searches
LD_DEBUG=libs bin/godp-server 2>&1 | head -40godp can be deployed either natively as a Systemd background service or containerized via Docker / Docker Compose. In both cases the SDK libraries must travel with the application; see SDK Library Installation.
Running as a native systemd daemon offers the lowest possible latency and minimal memory footprint (~0.25 MB).
# Compile and install service automatically
# (also deploys lib/x64 to /opt/godp/lib/x64)
make install-systemd-
Build the production binary:
make build
-
Deploy binary and shared libraries to
/opt/godp:sudo mkdir -p /opt/godp/bin /opt/godp/lib sudo cp bin/godp-server /opt/godp/bin/ sudo cp -a lib/x64 /opt/godp/lib/
-
Create unprivileged system user:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin godp sudo chown -R godp:godp /opt/godp sudo chmod -R 755 /opt/godp
-
Install and activate the systemd unit:
sudo cp systemd/godp.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable godp.service sudo systemctl start godp.service -
Verify the libraries resolve, then check service status and stream live logs:
# No "not found" lines expected ldd /opt/godp/bin/godp-server | grep -i "not found" # Check service status sudo systemctl status godp.service # Stream live JSON structured logs sudo journalctl -u godp.service -f
sudo systemctl restart godp.service # Restart
sudo systemctl stop godp.service # Stop
sudo systemctl disable godp.service # Disable autostartDeploying via Docker isolates the application and bundled SDK dependencies inside a container. No SDK installation is required on the host.
Start the service in detached mode:
docker compose up -d --buildCheck logs:
docker compose logs -fStop the service:
docker compose downBuild and run container:
# Build multi-stage image
make docker-build
# Run container exposing port 5050
make docker-runOr execute directly:
docker build -t godp:latest .
docker run -d --name godp-app -p 5050:5050 --restart unless-stopped godp:latestgodp is configured via environment variables (or lines in godp.service / docker-compose.yml):
| Variable | Default | Description |
|---|---|---|
PORT |
5050 |
HTTP service listening port |
HOST |
0.0.0.0 |
Network interface binding |
API_KEY |
(empty) | Optional API key required in X-API-Key or Authorization: Bearer |
MAX_REQUEST_SIZE_MB |
15 |
Maximum incoming request body limit |
CORS_ALLOW_ORIGIN |
* |
Allowed CORS origins |
READ_TIMEOUT |
15s |
Maximum duration for reading the entire request |
WRITE_TIMEOUT |
30s |
Maximum duration before timing out writes |
LD_LIBRARY_PATH |
(unset) | Optional. Extra search path for the SDK libraries; only needed if the $ORIGIN layout cannot be used |
All endpoints follow the uniform { "success": bool, "data"/"error": {...} } schema:
| Endpoint | Method | Description |
|---|---|---|
/health |
GET |
Health check & resource metrics |
/api/v1/fmd/extract |
POST |
Extract ISO/ANSI FMD from raw 8bpp grayscale image bytes |
/api/v1/fmd/enroll |
POST |
Enrollment: Fuses 2+ raw captures into 1 DP_REG_FEATURES template + JPEG preview of the last capture |
/api/v1/fmd/compare |
POST |
1:1 Verification: Compare probe FMD vs enrolled FMD |
/api/v1/fmd/identify |
POST |
1:N Search: Search probe FMD across candidate list |
/api/v1/fmd/convert |
POST |
Format conversion (ANSI 378 ⇄ ISO 19794-2) |
/api/v1/quality/nfiq |
POST |
NIST NFIQ fingerprint quality assessment (1–5) |
Enrollment note:
/api/v1/fmd/enrolltakes raw captures (DigitalPersona WebSDK capture JSON, or plain base64 8bpp pixels withwidth/height), not PNG/JPEG images or FMDs. ISO/ANSI templates cannot be fused. The output is a DigitalPersona proprietary template, so it is not interchangeable with ISO/ANSI FMDs.
Detailed request payloads, schemas, and curl examples are documented in docs/API.md.
DigitalPersona U.are.U shared libraries (lib/x64) and headers (lib/include) are proprietary software copyright © Crossmatch / DigitalPersona, Inc.
The Go wrapper implementation is proprietary and confidential.