License v1 Package
Shared v1 licensing package for the Ferqon ecosystem. This is the single source of truth for:
- License schema (JSON Schema)
- Tier definitions and feature sets
- Error codes
- Canonicalization logic (matches exactly between TypeScript and Python)
- Shared constants (
constants.ts/constants.py) - Type generators for both languages
- Cross-repo license API contract (
api-contract.json)
Structure
packages/license-v1/
├── api-contract.json # Single source of truth for license API routes, env vars, HMAC rules
├── api-contract.ts # Auto-generated TypeScript consumer (DO NOT EDIT)
├── api_contract.py # Auto-generated Python consumer (DO NOT EDIT)
├── schema.v1.json # JSON Schema for v1 license payload
├── tier-table.json # Tier definitions, features, activation limits, grace periods
├── error-codes.json # Error code definitions with HTTP status and messages
├── canonicalize.ts # TypeScript canonicalization implementation
├── canonicalize.py # Python canonicalization implementation (MUST match TS)
├── generate-api-contract.ts# Generator for api-contract.ts and api_contract.py
├── generate-types.ts # TypeScript type/Zod generator
├── gen.py # Python Pydantic/constant generator
├── generated-types.ts # Auto-generated TypeScript types (DO NOT EDIT)
├── generated-zod.ts # Auto-generated Zod schemas (DO NOT EDIT)
├── generated_models.py # Auto-generated Pydantic models (DO NOT EDIT)
├── generated_constants.py # Auto-generated Python constants (DO NOT EDIT)
├── vectors/ # Test vectors for cross-repo contract tests
└── README.md # This file
Canonicalization Rules
Both TypeScript and Python implementations use identical rules:
- Exclude
signaturefield from canonical payload - Sort object keys alphabetically
- Sort array items (features, fingerprints) for deterministic output
- Compact JSON with no whitespace
- UTF-8 encoding
- Timestamps in ISO 8601 format with
Zsuffix (no milliseconds) - Lowercase hex for fingerprint strings
Generators
TypeScript
cd packages/license-v1
npm install
npm run gen # Generates generated-types.ts and generated-zod.ts
Python
cd packages/license-v1
python -m license_v1.gen # Generates generated_models.py and generated_constants.py
API contract (TypeScript + Python)
cd packages/license-v1
node --import /path/to/tsx/dist/loader.mjs generate-api-contract.ts
Then sync the generated files into the website and Ferqon:
# Website
cd revyrlabs_website && pnpm run license-v1:sync
# Ferqon
cd Ferqon && python services/backend/scripts/sync_license_v1.py
Usage
TypeScript (Website)
import { LicensePayload, canonicalize, TIER_FEATURES, API_CONTRACT } from '@revyrlabs/license-v1';
const payload: LicensePayload = {
v: 1,
license_id: "FERQON-2026-ABCD-1234",
tier: "free",
// ...
};
const canonical = canonicalize(payload);
// Sign canonical bytes with Ed25519
// Use the API contract to avoid hardcoded env var names and route paths
const secret = process.env[API_CONTRACT.env.SERVER_HMAC_SECRET];
Python (Ferqon Backend)
from license_v1 import canonicalize, TIER_FEATURES
from license_v1.api_contract import API_CONTRACT
payload = {
"v": 1,
"license_id": "FERQON-2026-ABCD-1234",
"tier": "free",
# ...
}
canonical = canonicalize(payload)
# Sign canonical bytes with Ed25519
secret = os.environ.get(API_CONTRACT["env"]["SERVER_HMAC_SECRET"])
Invariant Enforcement
This package enforces the single-version v1 invariant:
- Schema explicitly requires
v: 1 - No versioned key selection logic
- No key_version field
- Single pair of environment variables:
FERQON_LICENSE_PRIVATE_KEY,FERQON_LICENSE_PUBLIC_KEY
Hidden Obstacles When Adding New Cross-Repo API Surface
When adding a new API endpoint or field that both the Revyr Labs website and the Ferqon backend/CLI must consume, the following pitfalls are easy to miss and will make future additions harder:
1. Version drift between the token and the envelope
- The license token (
signed_license) isv: 1and is Ed25519-signed.Ferqonlicense.pyexpectsv: 1. - The license key envelope (
buildEnvelope) isv: 1and is HMAC-signed with thelicense_keyas secret.FerqonCLIauth.pyexpectsv: 1. - Do not bump one version and assume the other follows. The two formats are intentionally separate.
2. HMAC key and message format ambiguity
- Heartbeat HMAC uses
FERQON_SERVER_HMAC_SECRETand message${license_id}:${deployment_id}:${timestamp}. - Challenge proof HMAC uses the
license_keyas secret and message${nonce}:${deployment_id}. - These are different secrets and different formats. Centralize them in
api-contract.jsonand regenerate the consumers.
3. License ID normalization
- License IDs are displayed as
FERQON-YYYY-XXXX-XXXX(uppercase). - The website normalizes incoming
license_idto uppercase before lookup. - Any new route that accepts
license_idmust do the same normalization, or the same license will fail to match in one repo.
4. DB column vs. display name vs. internal UUID
licenses.idis the internal UUID.licenses.license_idis the human/display ID.licenses.license_keyis the secret opaque key (ferqon_LK_...).- New API fields must be explicit about which identifier they accept and return.
5. Mock, local-real, and production data stores
local-mockusessrc/lib/services/mock-data.ts(in-memory, keyed byclerk_user_id).local-real(withUSE_LOCAL_DB=true) usessrc/lib/supabase/local.ts(in-memory tables).- Production uses real Supabase.
- Any new table or RPC must be added to both
local.tsand the real Supabase schema/migrations.
6. RPC parity in local.ts
local.tsmust mirror production RPCs likeactivate_deployment,evaluate_clone_status,mark_stripe_event_processed, andincrement_stripe_event_attempt.- If the real backend adds a new RPC, add a matching implementation to
local.tsor local tests and the website dev server will fail.
7. Env var naming mismatches
api-contract.jsonenv names are the canonical mapping: e.g.SERVER_HMAC_SECRET->FERQON_SERVER_HMAC_SECRET.- Do not hardcode env var names in
server-utils.ts,signer.ts, or Python. UseAPI_CONTRACT.env[...]. .env.example,.env.test, andtests/setup/env.tsmust all be kept in sync when an env var is added.
8. Route paths in generated code
api-contract.jsondefinesroutes.licenseHeartbeat.pathetc.- The website still uses Next.js file-based routes, so the file path and the contract path must match.
- If you change one, change the other, and regenerate
api-contract.tsandapi_contract.py.
9. Challenge nonce TTL and shape
api-contract.jsonchallenge.nonceTtlSecondsis 300 (5 minutes).- The
ChallengeRequestfingerprintis optional because the Ferqon CLI only sends it when available. - Changing the schema without updating
api-contract.jsonand the generated consumers will break the CLI.
10. Signing must stay offline
- API route handlers (
/api/license/activate,/api/license/heartbeat) must returnlicense.signed_licensefrom the DB. - They must not call
signLicenseTokenwith the private key. The private key is only for the offline signer (scripts/license/sign-pending.ts) and mock/dev fallbacks. - If a new route needs to hand out a signed token, it must read from the
signed_licensecolumn.
Contract Tests
The vectors/ directory contains test vectors used by cross-repo contract tests to ensure:
- Canonicalization produces identical bytes in both languages
- Signature verification works across language boundaries
- Tier definitions are consistent
- Error codes are consistent
- API contract routes and HMAC rules are consistent
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ferqon_license_contract-1.0.0.tar.gz.
File metadata
- Download URL: ferqon_license_contract-1.0.0.tar.gz
- Upload date:
- Size: 40.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
30caa1216dda9b30b9b3ebc76f471b14bcb534294822a9d3fab8f0e91ee2b46a
|
|
| MD5 |
adc745b8325798f40c0749d6e7318b31
|
|
| BLAKE2b-256 |
994a667bceab783dabe7e6cedb45323fd41971ac606abb85d1bd88140ac701f2
|
File details
Details for the file ferqon_license_contract-1.0.0-py3-none-any.whl.
File metadata
- Download URL: ferqon_license_contract-1.0.0-py3-none-any.whl
- Upload date:
- Size: 31.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1cab45ec4feed6e3a9beab3f8035d2fa878016189abadb322bb8c72fc4be7cba
|
|
| MD5 |
347f38a273c32da166f639096fd4d071
|
|
| BLAKE2b-256 |
9f635ddb0746114cad34aa84e61356f8a9f23dd10be67e178c06dfd177c70ea8
|