@o-lang/olang 1.2.19 → 1.2.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o-lang/olang",
3
- "version": "1.2.19",
3
+ "version": "1.2.20",
4
4
  "author": "Olalekan Ogundipe <info@olang.cloud>",
5
5
  "description": "O-Lang: A governance language for user-directed, rule-enforced agent workflows",
6
6
  "main": "./src/runtime/index.js",
@@ -1,89 +1,91 @@
1
1
  // src/runtime/index.js
2
2
  const { RuntimeAPI } = require('./RuntimeAPI');
3
- const { parse } = require('../parser');
4
- const crypto = require('crypto');
5
- const fs = require('fs');
6
- const path = require('path');
3
+ const { parse } = require('../parser');
4
+ const crypto = require('crypto');
5
+ const fs = require('fs');
6
+ const path = require('path');
7
7
 
8
- // Load kernel private key (cached)
8
+ // ── Load kernel private key (cached) ─────────────────────────────────────────
9
9
  let KERNEL_PRIVATE_KEY = null;
10
+
10
11
  function getKernelPrivateKey() {
11
12
  if (KERNEL_PRIVATE_KEY) return KERNEL_PRIVATE_KEY;
12
-
13
- const keyPath = process.env.KERNEL_PRIVATE_KEY_PATH || './kernel-keys/kernel-private.pem';
14
- const absolutePath = path.isAbsolute(keyPath) ? keyPath : path.join(process.cwd(), keyPath);
15
-
13
+
14
+ const keyPath = process.env.KERNEL_PRIVATE_KEY_PATH || './kernel-keys/kernel-private.pem';
15
+ const absolutePath = path.isAbsolute(keyPath)
16
+ ? keyPath
17
+ : path.join(process.cwd(), keyPath);
18
+
16
19
  if (!fs.existsSync(absolutePath)) {
17
20
  console.warn('[kernel] Warning: Private key not found at', absolutePath);
18
21
  console.warn('[kernel] Audit entries will NOT be signed');
19
22
  return null;
20
23
  }
21
-
24
+
22
25
  KERNEL_PRIVATE_KEY = fs.readFileSync(absolutePath, 'utf8');
23
26
  console.log('[kernel] ✅ Private key loaded for signing');
24
27
  return KERNEL_PRIVATE_KEY;
25
28
  }
26
29
 
27
- // Sign audit data with ED25519 (Node.js crypto.sign)
30
+ // ── Sign audit data with RSA-SHA256 ──────────────────────────────────────────
31
+ // Uses RSA-SHA256 to match the RSA key generated by crypto.generateKeyPairSync.
32
+ // The same sorted JSON serialization is used during verification.
33
+
28
34
  function signAuditData(auditData, privateKeyPem) {
29
35
  if (!privateKeyPem) return null;
30
-
36
+
31
37
  try {
32
- // Serialize audit data EXACTLY as it will be verified (sorted keys)
38
+ // Serialize with sorted keys must match verifySignature() in server.js
33
39
  const serialized = JSON.stringify(auditData, Object.keys(auditData).sort());
34
-
35
- // ED25519 signing: use crypto.sign() directly
36
- const signature = crypto.sign(
37
- null, // For ED25519, hash algorithm is implicit
38
- Buffer.from(serialized, 'utf8'),
39
- {
40
- key: privateKeyPem,
41
- dsaEncoding: 'ieee-p1363', // Required for ED25519
42
- }
43
- );
44
-
45
- return signature.toString('hex');
40
+
41
+ const sign = crypto.createSign('SHA256');
42
+ sign.update(serialized);
43
+ sign.end();
44
+
45
+ return sign.sign(privateKeyPem, 'hex');
46
46
  } catch (err) {
47
47
  console.error('[kernel] Signature error:', err.message);
48
48
  return null;
49
49
  }
50
50
  }
51
51
 
52
+ // ── Main execute function ─────────────────────────────────────────────────────
53
+
52
54
  async function execute(workflow, inputs, agentResolver, verbose = false) {
53
55
  const rt = new RuntimeAPI({ verbose });
54
-
55
- // run the workflow — result only contains Return values
56
+
57
+ // Run the workflow — result contains only Return values
56
58
  const result = await rt.executeWorkflow(workflow, inputs, agentResolver);
57
59
 
58
- // rt is still alive here grab audit before it dies
60
+ // Grab audit data while RuntimeAPI instance is still alive
59
61
  const lastEntry = rt.auditLog.at(-1);
60
62
  const firstEntry = rt.auditLog.at(0);
61
63
 
62
- // Build audit object BEFORE signing
64
+ // Build audit object BEFORE signing — must match verifySignature() structure
63
65
  const auditData = {
64
- execution_hash: lastEntry?.hash ?? null,
65
- previous_hash: firstEntry?.hash ?? 'GENESIS',
66
+ execution_hash: lastEntry?.hash ?? null,
67
+ previous_hash: firstEntry?.hash ?? 'GENESIS',
66
68
  merkle_root: rt._calculateMerkleRoot(),
67
- kernel_version: lastEntry?.details?.kernel_version ?? null,
69
+ kernel_version: lastEntry?.details?.kernel_version ?? null,
68
70
  governance_profile_hash: lastEntry?.details?.governance_profile_hash ?? null,
69
71
  integrity: rt.verifyAuditLogIntegrity(),
70
72
  chain: rt.auditLog,
71
73
  };
72
74
 
73
- // SIGN the audit data
75
+ // Sign the audit data with the kernel private key
74
76
  const privateKey = getKernelPrivateKey();
75
- const signature = signAuditData(auditData, privateKey);
76
-
77
- // Load public key for verification (optional but helpful)
77
+ const signature = signAuditData(auditData, privateKey);
78
+
79
+ // Load public key for client-side verification (stripped PEM)
78
80
  let publicKey = null;
79
81
  if (privateKey) {
80
82
  try {
81
- const pubKeyPath = process.env.KERNEL_PUBLIC_KEY_PATH || './kernel-keys/kernel-public.pem';
82
- const absolutePubPath = path.isAbsolute(pubKeyPath)
83
- ? pubKeyPath
83
+ const pubKeyPath = process.env.KERNEL_PUBLIC_KEY_PATH || './kernel-keys/kernel-public.pem';
84
+ const absolutePubPath = path.isAbsolute(pubKeyPath)
85
+ ? pubKeyPath
84
86
  : path.join(process.cwd(), pubKeyPath);
87
+
85
88
  if (fs.existsSync(absolutePubPath)) {
86
- // Read and clean PEM for storage
87
89
  publicKey = fs.readFileSync(absolutePubPath, 'utf8')
88
90
  .replace('-----BEGIN PUBLIC KEY-----', '')
89
91
  .replace('-----END PUBLIC KEY-----', '')
@@ -94,10 +96,11 @@ async function execute(workflow, inputs, agentResolver, verbose = false) {
94
96
  }
95
97
  }
96
98
 
99
+ // Attach audit to result — server.js detaches it before sending to client
97
100
  result.__audit = {
98
101
  ...auditData,
99
- signature, // Cryptographic signature (hex string)
100
- publicKey, // Public key for verification (cleaned PEM)
102
+ signature, // RSA-SHA256 hex signature
103
+ publicKey, // Stripped PEM for client verification
101
104
  };
102
105
 
103
106
  return result;