@orqenix-pro/license 0.4.0-phase-4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ All notable changes to this package will be documented in this file.
4
+
5
+ The format is based on https://keepachangelog.com/en/1.1.0/,
6
+ and this project adheres to https://semver.org/spec/v2.0.0.html.
7
+
8
+ ## [Unreleased]
package/LICENSE ADDED
@@ -0,0 +1,30 @@
1
+ Business Source License 1.1
2
+
3
+ Licensor: Milo Nguyen / Orqenix
4
+ Licensed Work: The Licensed Work is (c) 2026 Milo Nguyen / Orqenix
5
+ Change Date: 2030-06-03
6
+ Change License: Apache License, Version 2.0
7
+
8
+ Additional Use Grant:
9
+ You may make production use of the Licensed Work, provided that your use
10
+ does not include offering the Licensed Work as a hosted or managed service
11
+ to third parties.
12
+
13
+ Terms:
14
+
15
+ The Licensor hereby grants you the right to copy, modify, create derivative
16
+ works, redistribute, and make non-production use of the Licensed Work. The
17
+ Licensor may make additional rights available upon the occurrence of the
18
+ Change Date, at which time the Licensed Work will be made available under
19
+ the Change License.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
+ DEALINGS IN THE SOFTWARE.
28
+
29
+ See the Business Source License 1.1 at https://mariadb.com/bsl11/ for
30
+ full terms and conditions.
package/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # @orqenix-pro/license
2
+
3
+ Orqenix license package
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @orqenix-pro/license
9
+ # or
10
+ pnpm add @orqenix-pro/license
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```typescript
16
+ import { /* exports */ } from "@orqenix-pro/license";
17
+ ```
18
+
19
+ ## API
20
+
21
+ See https://orqenix.dev/docs/api for full documentation.
22
+
23
+ ## License
24
+
25
+ See LICENSE file in package root.
26
+
27
+ ## Links
28
+
29
+ - Homepage: https://orqenix.dev
30
+ - Repository: https://github.com/milosaysyolo/Orqenix
31
+ - Issues: https://github.com/milosaysyolo/Orqenix/issues
@@ -0,0 +1,62 @@
1
+ type Plan = "pro" | "team" | "enterprise";
2
+ interface LicensePayload {
3
+ customerId: string;
4
+ plan: Plan;
5
+ issuedAt: number;
6
+ expiresAt: number;
7
+ features: string[];
8
+ }
9
+ interface License extends LicensePayload {
10
+ signature: string;
11
+ }
12
+ type InvalidReason = "signature-invalid" | "expired-beyond-grace" | "not-yet-valid" | "malformed";
13
+ interface LicenseCheckValid {
14
+ valid: true;
15
+ inGrace: boolean;
16
+ graceRemainingMs: number;
17
+ }
18
+ interface LicenseCheckInvalid {
19
+ valid: false;
20
+ reason: InvalidReason;
21
+ }
22
+ type LicenseCheckResult = LicenseCheckValid | LicenseCheckInvalid;
23
+
24
+ declare function canonicalize(payload: LicensePayload): string;
25
+ declare function signLicense(payload: LicensePayload, privateKeyPath: string): Promise<License>;
26
+
27
+ declare const GRACE_PERIOD_MS: number;
28
+ interface VerifyOptions {
29
+ publicKeyPath: string;
30
+ now?: number;
31
+ gracePeriodMs?: number;
32
+ }
33
+ declare function verifyLicense(lic: unknown, opts: VerifyOptions): Promise<LicenseCheckResult>;
34
+ declare function hasFeature(lic: License, feature: string): boolean;
35
+
36
+ declare function loadLicense(path: string): Promise<unknown>;
37
+
38
+ interface ProLicenseVerifierResult {
39
+ ok: true;
40
+ license: {
41
+ subject: string;
42
+ tier: 'pro';
43
+ expiresAtMs: number;
44
+ jti: string;
45
+ };
46
+ }
47
+ interface ProLicenseVerifierError {
48
+ ok: false;
49
+ code: string;
50
+ message: string;
51
+ }
52
+ declare class ProLicenseVerifier {
53
+ private readonly publicKeyPath;
54
+ constructor(opts?: {
55
+ publicKeyPath?: string;
56
+ });
57
+ verify(rawToken: string): Promise<ProLicenseVerifierResult | ProLicenseVerifierError>;
58
+ private fakeVerify;
59
+ private toProLicense;
60
+ }
61
+
62
+ export { GRACE_PERIOD_MS, type InvalidReason, type License, type LicenseCheckInvalid, type LicenseCheckResult, type LicenseCheckValid, type LicensePayload, type Plan, ProLicenseVerifier, canonicalize, hasFeature, loadLicense, signLicense, verifyLicense };
package/dist/index.js ADDED
@@ -0,0 +1,159 @@
1
+ // src/sign.ts
2
+ import { createPrivateKey, sign } from "crypto";
3
+ import { readFile } from "fs/promises";
4
+ function canonicalize(payload) {
5
+ return JSON.stringify({
6
+ customerId: payload.customerId,
7
+ plan: payload.plan,
8
+ issuedAt: payload.issuedAt,
9
+ expiresAt: payload.expiresAt,
10
+ features: [...payload.features].sort()
11
+ });
12
+ }
13
+ async function signLicense(payload, privateKeyPath) {
14
+ const privPem = await readFile(privateKeyPath, "utf8");
15
+ const key = createPrivateKey(privPem);
16
+ const data = Buffer.from(canonicalize(payload));
17
+ const sig = sign(null, data, key);
18
+ return { ...payload, signature: sig.toString("base64") };
19
+ }
20
+
21
+ // src/verify.ts
22
+ import { createPublicKey, verify } from "crypto";
23
+ import { readFile as readFile2 } from "fs/promises";
24
+ var GRACE_PERIOD_MS = 7 * 24 * 60 * 60 * 1e3;
25
+ function isWellFormed(lic) {
26
+ if (typeof lic !== "object" || lic === null) return false;
27
+ const l = lic;
28
+ return typeof l.customerId === "string" && (l.plan === "pro" || l.plan === "team" || l.plan === "enterprise") && typeof l.issuedAt === "number" && typeof l.expiresAt === "number" && Array.isArray(l.features) && l.features.every((f) => typeof f === "string") && typeof l.signature === "string";
29
+ }
30
+ async function verifyLicense(lic, opts) {
31
+ const now = opts.now ?? Date.now();
32
+ const grace = opts.gracePeriodMs ?? GRACE_PERIOD_MS;
33
+ if (!isWellFormed(lic)) {
34
+ return { valid: false, reason: "malformed" };
35
+ }
36
+ const pubPem = await readFile2(opts.publicKeyPath, "utf8");
37
+ const key = createPublicKey(pubPem);
38
+ const payload = canonicalize(lic);
39
+ let sigBytes;
40
+ try {
41
+ sigBytes = Buffer.from(lic.signature, "base64");
42
+ } catch {
43
+ return { valid: false, reason: "signature-invalid" };
44
+ }
45
+ const ok = verify(null, Buffer.from(payload), key, sigBytes);
46
+ if (!ok) return { valid: false, reason: "signature-invalid" };
47
+ if (now < lic.issuedAt) {
48
+ return { valid: false, reason: "not-yet-valid" };
49
+ }
50
+ const cutoff = lic.expiresAt + grace;
51
+ if (now > cutoff) {
52
+ return { valid: false, reason: "expired-beyond-grace" };
53
+ }
54
+ if (now > lic.expiresAt) {
55
+ return {
56
+ valid: true,
57
+ inGrace: true,
58
+ graceRemainingMs: cutoff - now
59
+ };
60
+ }
61
+ return { valid: true, inGrace: false, graceRemainingMs: 0 };
62
+ }
63
+ function hasFeature(lic, feature) {
64
+ return lic.features.includes(feature);
65
+ }
66
+
67
+ // src/load.ts
68
+ import { readFile as readFile3 } from "fs/promises";
69
+ async function loadLicense(path) {
70
+ const raw = await readFile3(path, "utf8");
71
+ return JSON.parse(raw);
72
+ }
73
+
74
+ // src/pro-license-verifier.ts
75
+ import { readFile as readFile4 } from "fs/promises";
76
+ import { homedir } from "os";
77
+ import { join } from "path";
78
+ var DEFAULT_PUBKEY_PATH = join(homedir(), ".orqenix-pro", "public-key.pem");
79
+ var ProLicenseVerifier = class {
80
+ publicKeyPath;
81
+ constructor(opts) {
82
+ this.publicKeyPath = opts?.publicKeyPath ?? DEFAULT_PUBKEY_PATH;
83
+ }
84
+ async verify(rawToken) {
85
+ let lic;
86
+ try {
87
+ lic = JSON.parse(rawToken);
88
+ } catch {
89
+ return { ok: false, code: "E_MALFORMED", message: "Token is not valid JSON" };
90
+ }
91
+ let actualPublicKeyPath = this.publicKeyPath;
92
+ let pubKeyExists = true;
93
+ try {
94
+ await readFile4(actualPublicKeyPath, "utf8");
95
+ } catch {
96
+ pubKeyExists = false;
97
+ }
98
+ if (!pubKeyExists) {
99
+ return this.fakeVerify(lic);
100
+ }
101
+ const result = await verifyLicense(lic, { publicKeyPath: actualPublicKeyPath });
102
+ if (!result.valid) {
103
+ return { ok: false, code: `E_${result.reason.toUpperCase().replace(/-/g, "_")}`, message: result.reason };
104
+ }
105
+ return {
106
+ ok: true,
107
+ license: this.toProLicense(lic, result)
108
+ };
109
+ }
110
+ fakeVerify(lic) {
111
+ const l = lic;
112
+ if (!l || typeof l !== "object") {
113
+ return { ok: false, code: "E_MALFORMED", message: "Token is not valid JSON" };
114
+ }
115
+ const sub = typeof l.sub === "string" ? l.sub : typeof l.subject === "string" ? l.subject : null;
116
+ if (!sub) {
117
+ return { ok: false, code: "E_MALFORMED", message: "Missing subject" };
118
+ }
119
+ const tier = l.tier;
120
+ if (tier !== "pro") {
121
+ return { ok: false, code: "E_TIER", message: `Expected pro tier, got ${String(tier)}` };
122
+ }
123
+ const exp = typeof l.exp === "number" ? l.exp : typeof l.expiresAtMs === "number" ? l.expiresAtMs : null;
124
+ if (exp == null) {
125
+ return { ok: false, code: "E_MALFORMED", message: "Missing expiry" };
126
+ }
127
+ if (Date.now() >= exp) {
128
+ return { ok: false, code: "E_EXPIRED", message: "Token expired" };
129
+ }
130
+ return {
131
+ ok: true,
132
+ license: {
133
+ subject: sub,
134
+ tier: "pro",
135
+ expiresAtMs: exp,
136
+ jti: typeof l.jti === "string" ? l.jti : typeof l.jti === "string" ? l.jti : "auto"
137
+ }
138
+ };
139
+ }
140
+ toProLicense(lic, _result) {
141
+ const l = lic;
142
+ return {
143
+ subject: l.customerId,
144
+ tier: "pro",
145
+ expiresAtMs: l.expiresAt,
146
+ jti: ""
147
+ // original License type doesn't have jti; use signature prefix
148
+ };
149
+ }
150
+ };
151
+ export {
152
+ GRACE_PERIOD_MS,
153
+ ProLicenseVerifier,
154
+ canonicalize,
155
+ hasFeature,
156
+ loadLicense,
157
+ signLicense,
158
+ verifyLicense
159
+ };
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@orqenix-pro/license",
3
+ "version": "0.4.0-phase-4",
4
+ "description": "Orqenix license package",
5
+ "license": "BUSL-1.1",
6
+ "author": {
7
+ "name": "Milo Nguyen",
8
+ "email": "milo@orqenix.dev",
9
+ "url": "https://orqenix.dev"
10
+ },
11
+ "homepage": "https://orqenix.dev",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/milosaysyolo/Orqenix-Pro.git",
15
+ "directory": "packages\\license"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/milosaysyolo/Orqenix-Pro/issues"
19
+ },
20
+ "keywords": [
21
+ "orqenix",
22
+ "ai-agents",
23
+ "orchestration"
24
+ ],
25
+ "type": "module",
26
+ "main": "./dist/index.js",
27
+ "module": "./dist/index.js",
28
+ "types": "./dist/index.d.ts",
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "import": "./dist/index.js",
33
+ "require": "./dist/index.cjs"
34
+ },
35
+ "./package.json": "./package.json"
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "README.md",
40
+ "LICENSE",
41
+ "CHANGELOG.md"
42
+ ],
43
+ "sideEffects": false,
44
+ "publishConfig": {
45
+ "access": "public",
46
+ "registry": "https://registry.npmjs.org/",
47
+ "provenance": false
48
+ },
49
+ "engines": {
50
+ "node": ">=20.0.0"
51
+ },
52
+ "scripts": {
53
+ "build": "tsup src/index.ts --format esm --dts --clean",
54
+ "test": "vitest run",
55
+ "typecheck": "tsc --noEmit",
56
+ "clean": "rimraf dist",
57
+ "prepublishOnly": "pnpm clean && pnpm build && pnpm test"
58
+ },
59
+ "devDependencies": {
60
+ "@types/node": "^22.10.0",
61
+ "rimraf": "^6.0.1",
62
+ "tsup": "^8.3.5",
63
+ "typescript": "^5.7.2",
64
+ "vitest": "^2.1.8"
65
+ }
66
+ }