@sdotwinter/openclaw-deterministic 0.14.0 → 0.16.0

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/bin/doctor.js CHANGED
@@ -3,6 +3,7 @@
3
3
  const fs = require("fs");
4
4
  const os = require("os");
5
5
  const path = require("path");
6
+ const crypto = require("crypto");
6
7
 
7
8
  const JSON_MODE = process.argv.includes("--json");
8
9
 
@@ -88,6 +89,44 @@ Details: ${event.details}
88
89
  }
89
90
  }
90
91
 
92
+ // -----------------------------
93
+ // Canonical Integrity Verification
94
+ // -----------------------------
95
+
96
+ function sha256(content) {
97
+ return crypto.createHash("sha256").update(content).digest("hex");
98
+ }
99
+
100
+ function extractHash(content) {
101
+ const match = content.match(/Canonical-Hash:\s*SHA256:([a-f0-9]+)/);
102
+ return match ? match[1] : null;
103
+ }
104
+
105
+ function stripHeaders(content) {
106
+ return content.replace(/<!--[\s\S]*?-->/g, "").trim();
107
+ }
108
+
109
+ function verifyIntegrity(filePath) {
110
+ if (!exists(filePath)) {
111
+ return { present: false };
112
+ }
113
+
114
+ const raw = read(filePath);
115
+ const storedHash = extractHash(raw);
116
+
117
+ if (!storedHash) {
118
+ return { present: true, valid: false };
119
+ }
120
+
121
+ const stripped = stripHeaders(raw);
122
+ const currentHash = sha256(stripped);
123
+
124
+ return {
125
+ present: true,
126
+ valid: storedHash === currentHash,
127
+ };
128
+ }
129
+
91
130
  function evaluate() {
92
131
  const result = {
93
132
  cliVersion: CLI_VERSION,
@@ -106,6 +145,26 @@ function evaluate() {
106
145
  soul: exists(paths.soul),
107
146
  };
108
147
 
148
+ // -----------------------------
149
+ // Canonical Integrity Checks
150
+ // -----------------------------
151
+
152
+ result.integrity = {
153
+ operating: verifyIntegrity(paths.operating),
154
+ detSoul: verifyIntegrity(paths.detSoul),
155
+ compactor: verifyIntegrity(paths.compactor),
156
+ };
157
+
158
+ // Log governance violations if hashes fail
159
+ for (const [key, status] of Object.entries(result.integrity)) {
160
+ if (status.present && status.valid === false) {
161
+ appendGovernanceEvent({
162
+ type: "canonical-hash-mismatch",
163
+ details: `${key} failed canonical integrity verification.`,
164
+ });
165
+ }
166
+ }
167
+
109
168
  const cfg = readJsonSafe(paths.config);
110
169
 
111
170
  result.config = {
@@ -171,6 +230,20 @@ function printHuman(r) {
171
230
  }
172
231
  }
173
232
 
233
+ if (r.integrity) {
234
+ console.log("\nCanonical Integrity:");
235
+
236
+ for (const [key, status] of Object.entries(r.integrity)) {
237
+ if (!status.present) {
238
+ console.log(`⚠ ${key} not present (cannot verify).`);
239
+ } else if (status.valid) {
240
+ console.log(`✅ ${key} integrity verified.`);
241
+ } else {
242
+ console.log(`❌ ${key} integrity FAILED.`);
243
+ }
244
+ }
245
+ }
246
+
174
247
  if (!r.config.present) {
175
248
  console.log("⚠ Deterministic config missing. Using defaults.");
176
249
  } else if (r.config.invalid) {
package/bin/install.js CHANGED
@@ -3,6 +3,7 @@
3
3
  const fs = require("fs");
4
4
  const os = require("os");
5
5
  const path = require("path");
6
+ const crypto = require("crypto");
6
7
 
7
8
  // -----------------------------
8
9
  // Args
@@ -87,10 +88,13 @@ function readFile(p) {
87
88
  }
88
89
 
89
90
  function timestamp() {
90
- // keep filesystem-safe
91
91
  return new Date().toISOString().replace(/:/g, "-");
92
92
  }
93
93
 
94
+ function sha256(content) {
95
+ return crypto.createHash("sha256").update(content).digest("hex");
96
+ }
97
+
94
98
  function backupSnapshot(pathsToBackup) {
95
99
  if (DRY_RUN) {
96
100
  console.log("[DRY-RUN] Would create backup snapshot.");
@@ -114,13 +118,22 @@ function backupSnapshot(pathsToBackup) {
114
118
  return snap;
115
119
  }
116
120
 
121
+ // -----------------------------
122
+ // Canonical Install Function
123
+ // -----------------------------
117
124
  function copyWithVersionStamp(src, dest) {
118
125
  const raw = readFile(src);
119
- const stamped = `${VERSION_STAMP}\n${raw.replace(/^\uFEFF/, "")}`;
126
+ const clean = raw.replace(/^\uFEFF/, "");
127
+ const hash = sha256(clean);
128
+
129
+ const stamped = `${VERSION_STAMP}
130
+ <!-- Canonical-Hash: SHA256:${hash} -->
131
+
132
+ ${clean}`;
133
+
120
134
  writeFile(dest, stamped);
121
135
 
122
136
  if (!DRY_RUN) {
123
- // keep output consistent with your CLI style
124
137
  const pretty = dest.startsWith(workspace)
125
138
  ? dest.replace(workspace + path.sep, "")
126
139
  : dest;
@@ -134,7 +147,6 @@ function copyWithVersionStamp(src, dest) {
134
147
  // Install steps
135
148
  // -----------------------------
136
149
  function installTemplates() {
137
- // Ensure skill directory exists
138
150
  ensureDir(path.dirname(target.compactor));
139
151
 
140
152
  copyWithVersionStamp(tpl("OPERATING_RULES.md"), target.operating);
@@ -205,8 +217,6 @@ const pathsToBackup = [
205
217
  target.operating,
206
218
  target.detSoul,
207
219
  target.compactor,
208
- // NOTE: we do NOT back up SOUL.md here because install never modifies it.
209
- // If you later add an "enable" flow that edits SOUL.md, that command should back it up there.
210
220
  target.config,
211
221
  ];
212
222
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdotwinter/openclaw-deterministic",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
4
4
  "description": "Deterministic governance and memory compaction layer for OpenClaw",
5
5
  "keywords": [
6
6
  "openclaw",