Skip to main content

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:

  1. Exclude signature field from canonical payload
  2. Sort object keys alphabetically
  3. Sort array items (features, fingerprints) for deterministic output
  4. Compact JSON with no whitespace
  5. UTF-8 encoding
  6. Timestamps in ISO 8601 format with Z suffix (no milliseconds)
  7. 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) is v: 1 and is Ed25519-signed. Ferqon license.py expects v: 1.
  • The license key envelope (buildEnvelope) is v: 1 and is HMAC-signed with the license_key as secret. Ferqon CLI auth.py expects v: 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_SECRET and message ${license_id}:${deployment_id}:${timestamp}.
  • Challenge proof HMAC uses the license_key as secret and message ${nonce}:${deployment_id}.
  • These are different secrets and different formats. Centralize them in api-contract.json and regenerate the consumers.

3. License ID normalization

  • License IDs are displayed as FERQON-YYYY-XXXX-XXXX (uppercase).
  • The website normalizes incoming license_id to uppercase before lookup.
  • Any new route that accepts license_id must 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.id is the internal UUID.
  • licenses.license_id is the human/display ID.
  • licenses.license_key is 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-mock uses src/lib/services/mock-data.ts (in-memory, keyed by clerk_user_id).
  • local-real (with USE_LOCAL_DB=true) uses src/lib/supabase/local.ts (in-memory tables).
  • Production uses real Supabase.
  • Any new table or RPC must be added to both local.ts and the real Supabase schema/migrations.

6. RPC parity in local.ts

  • local.ts must mirror production RPCs like activate_deployment, evaluate_clone_status, mark_stripe_event_processed, and increment_stripe_event_attempt.
  • If the real backend adds a new RPC, add a matching implementation to local.ts or local tests and the website dev server will fail.

7. Env var naming mismatches

  • api-contract.json env 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. Use API_CONTRACT.env[...].
  • .env.example, .env.test, and tests/setup/env.ts must all be kept in sync when an env var is added.

8. Route paths in generated code

  • api-contract.json defines routes.licenseHeartbeat.path etc.
  • 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.ts and api_contract.py.

9. Challenge nonce TTL and shape

  • api-contract.json challenge.nonceTtlSeconds is 300 (5 minutes).
  • The ChallengeRequest fingerprint is optional because the Ferqon CLI only sends it when available.
  • Changing the schema without updating api-contract.json and the generated consumers will break the CLI.

10. Signing must stay offline

  • API route handlers (/api/license/activate, /api/license/heartbeat) must return license.signed_license from the DB.
  • They must not call signLicenseToken with 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_license column.

Contract Tests

The vectors/ directory contains test vectors used by cross-repo contract tests to ensure:

  1. Canonicalization produces identical bytes in both languages
  2. Signature verification works across language boundaries
  3. Tier definitions are consistent
  4. Error codes are consistent
  5. 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

ferqon_license_contract-1.0.0.tar.gz (40.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ferqon_license_contract-1.0.0-py3-none-any.whl (31.7 kB view details)

Uploaded Python 3

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

Hashes for ferqon_license_contract-1.0.0.tar.gz
Algorithm Hash digest
SHA256 30caa1216dda9b30b9b3ebc76f471b14bcb534294822a9d3fab8f0e91ee2b46a
MD5 adc745b8325798f40c0749d6e7318b31
BLAKE2b-256 994a667bceab783dabe7e6cedb45323fd41971ac606abb85d1bd88140ac701f2

See more details on using hashes here.

File details

Details for the file ferqon_license_contract-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for ferqon_license_contract-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1cab45ec4feed6e3a9beab3f8035d2fa878016189abadb322bb8c72fc4be7cba
MD5 347f38a273c32da166f639096fd4d071
BLAKE2b-256 9f635ddb0746114cad34aa84e61356f8a9f23dd10be67e178c06dfd177c70ea8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page