This document describes the ExactEDI licensing system, including license file format, signature verification, and the license generation workflow.
Overview
ExactEDI uses Ed25519 digital signatures to verify license authenticity. Each license file is cryptographically signed with a private key, and the signature is verified against a public key embedded in the binary.
Security Model
- Enforcement + Deterrence: Not unbreakable DRM, but strong enough to prevent casual bypass
- Offline-first: All verification happens locally with no network calls
- Signed licenses: Ed25519 signatures ensure license integrity and authenticity
- Graceful degradation: Falls back to trial mode when no valid license is found
License Tiers
| Tier | Features | Limits |
|---|---|---|
| Trial | parse, validate, explain | 14 days, 10 files/session |
| Standard | + normalize | Unlimited |
| Professional | + import, batch | Unlimited |
| Enterprise | + api, custom | Unlimited, custom terms |
License File Format
License files (.lic) consist of two parts:
- Line 1: Base64-encoded Ed25519 signature (64 bytes → ~88 characters)
- Lines 2+: JSON payload (the signed content)
Example License File
dGhpc2lzYWJhc2U2NGVuY29kZWRzaWduYXR1cmVoZXJl...==
{
"license_id": "550e8400-e29b-41d4-a716-446655440000",
"tier": "professional",
"organization": "Example Corp",
"email": "admin@example.com",
"issued_at": 1704067200,
"expires_at": 1735689600,
"max_files_per_session": 0,
"version": "1.*",
"features": ["parse", "validate", "explain", "normalize", "import", "batch"]
}
JSON Payload Fields
| Field | Type | Required | Description |
|---|---|---|---|
license_id | string | Yes | Unique license identifier (UUID format) |
tier | string | Yes | One of: trial, standard, professional, enterprise |
organization | string | No | Licensee organization name |
email | string | No | Licensee contact email |
issued_at | int64 | No | Issue timestamp (Unix epoch seconds) |
expires_at | int64 | No | Expiration timestamp (0 = never expires) |
max_files_per_session | uint32 | No | Per-session file limit (0 = unlimited) |
version | string | No | Engine version constraint (e.g., "1.*") |
features | array | No | Explicit feature list (overrides tier defaults) |
Feature Names
parse- Parse X12 to JSONLvalidate- Validate X12 structureexplain- Generate Facts JSONnormalize- Combined parse + factsimport- SQLite database importbatch- Batch file processingapi- API/SDK integrationcustom- Custom output formats
License File Search Order
ExactEDI searches for license files in this order:
- Path specified via
--license <path>CLI option EXACTEDI_LICENSEenvironment variable./exactedi.lic(current directory)%APPDATA%/ExactEDI/license.lic(Windows)~/.exactedi/license.lic(Unix-like)
Signature Verification
Algorithm
- Algorithm: Ed25519 (RFC 8032)
- Key size: 256-bit (32-byte public key, 64-byte secret key)
- Signature size: 64 bytes
- Library: libsodium
Verification Process
- Read license file, split into signature (line 1) and payload (remaining lines)
- Base64-decode the signature to 64 bytes
- Verify signature against payload using embedded public key
- If valid, parse JSON payload and check expiration
- If invalid or expired, fall back to trial mode
Embedded Public Key
The public key is compiled into the binary at build time. See src/exactedi/licensing/signature.cpp:
constexpr PublicKey EMBEDDED_PUBLIC_KEY = {{
0x01, 0x02, 0x03, ... // 32 bytes
}};
Important: Replace the placeholder key with your production public key before release!
License Generation Workflow
Prerequisites
- Build the
license-gentool (intools/license-gen/) - Ensure libsodium is available
Generate a Keypair
cd tools/bin
./license-gen --generate-keypair
This creates:
keypair.public- Public key (embed in binary)keypair.secret- Secret key (KEEP SECURE!)
Update the Embedded Public Key
Copy the generated public key into src/exactedi/licensing/signature.cpp:
constexpr PublicKey EMBEDDED_PUBLIC_KEY = {{
// Paste the C++ array format output from license-gen
}};
Rebuild the ExactEDI binary.
Create a License
-
Create a sample template:
./license-gen --create-sample -
Edit
license.jsonwith customer details -
Sign the license:
./license-gen --sign license.json -
Distribute
license.licto the customer
Verify a License
./license-gen --verify license.lic
Trial Mode
When no valid license is found, ExactEDI operates in trial mode:
- Duration: 14 days from first use
- File limit: 10 files per session
- Features: parse, validate, explain
Trial state is tracked in:
- Windows:
%APPDATA%/ExactEDI/.exactedi_trial - Unix:
~/.exactedi/.exactedi_trial
Error Messages
| Status | Message | Resolution |
|---|---|---|
LicenseStatus::Valid | License valid | None needed |
LicenseStatus::Trial | Trial mode active | Purchase license |
LicenseStatus::Expired | License has expired | Renew license |
LicenseStatus::Invalid | Signature verification failed | Re-download license |
LicenseStatus::Corrupted | License file malformed | Re-download license |
LicenseStatus::NotFound | No license file found | Install license |
Security Best Practices
- Protect the secret key: Never commit
keypair.secretto version control - Rotate keys periodically: Generate new keypairs for major releases
- Monitor license usage: Track license IDs to detect unauthorized sharing
- Time-limit licenses: Use
expires_atto ensure regular renewal - Version constraints: Use
versionfield to limit license to specific releases
Building the License Tool
cd tools/license-gen
cmake -B build -S . --preset=default
cmake --build build
The tool will be output to tools/bin/license-gen.