speclock 2.1.1 → 3.0.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/README.md CHANGED
@@ -58,6 +58,9 @@ No other tool does this. Not Claude's native memory. Not Mem0. Not CLAUDE.md fil
58
58
  | Git-aware (checkpoints, rollback) | No | No | No | **Yes** |
59
59
  | Drift detection | No | No | No | **Yes — scans changes against locks** |
60
60
  | CI/CD integration | No | No | No | **Yes — GitHub Actions** |
61
+ | **Hard enforcement (block violations)** | No | No | No | **Yes — hard mode blocks above threshold** |
62
+ | **API Key Auth + RBAC** | No | No | No | **Yes** |
63
+ | **Encrypted Storage (AES-256)** | No | No | No | **Yes** |
61
64
  | Multi-agent timeline | No | No | No | **Yes** |
62
65
  | Cross-platform | Claude only | MCP only | Tool-specific | **Universal (MCP + npm)** |
63
66
 
@@ -192,10 +195,10 @@ Result: NO CONFLICT (confidence: 7%)
192
195
  | Mode | Platforms | How It Works |
193
196
  |------|-----------|--------------|
194
197
  | **MCP Remote** | Lovable, bolt.diy, Base44 | Connect via URL — no install needed |
195
- | **MCP Local** | Claude Code, Cursor, Windsurf, Cline | `npx speclock serve` — 24 tools via MCP |
198
+ | **MCP Local** | Claude Code, Cursor, Windsurf, Cline | `npx speclock serve` — 28 tools via MCP |
196
199
  | **npm File-Based** | Bolt.new, Aider, Rocket.new | `npx speclock setup` — AI reads SPECLOCK.md + uses CLI |
197
200
 
198
- ## 24 MCP Tools
201
+ ## 28 MCP Tools
199
202
 
200
203
  ### Memory Management
201
204
  | Tool | Purpose |
@@ -249,6 +252,14 @@ Result: NO CONFLICT (confidence: 7%)
249
252
  | `speclock_verify_audit` | Verify HMAC audit chain integrity — tamper detection |
250
253
  | `speclock_export_compliance` | Generate SOC 2 / HIPAA / CSV compliance reports |
251
254
 
255
+ ### Hard Enforcement (v2.5)
256
+ | Tool | Purpose |
257
+ |------|---------|
258
+ | `speclock_set_enforcement` | Set enforcement mode: advisory (warn) or hard (block) |
259
+ | `speclock_override_lock` | Override a lock with justification — logged to audit trail |
260
+ | `speclock_semantic_audit` | Semantic pre-commit: analyze code changes vs locks |
261
+ | `speclock_override_history` | View lock override history for audit review |
262
+
252
263
  ## Auto-Guard: Locks That Actually Work
253
264
 
254
265
  When you add a lock, SpecLock **automatically finds and guards related files**:
@@ -317,6 +328,12 @@ speclock audit-verify # Verify HMAC audit chain integrity
317
328
  speclock export --format <soc2|hipaa|csv> # Compliance export
318
329
  speclock license # Show license tier and usage
319
330
 
331
+ # Hard Enforcement (v2.5)
332
+ speclock enforce <advisory|hard> # Set enforcement mode
333
+ speclock override <lockId> <reason> # Override a lock with justification
334
+ speclock overrides [--lock <id>] # Show override history
335
+ speclock audit-semantic # Semantic pre-commit audit
336
+
320
337
  # Other
321
338
  speclock status # Show brain summary
322
339
  speclock serve [--project <path>] # Start MCP server
@@ -372,6 +389,38 @@ SOC 2 reports include: constraint change history, access logs, decision audit tr
372
389
  ```
373
390
  Audits changed files against locks, posts PR comments, fails workflow on violations.
374
391
 
392
+ ## Hard Enforcement (v2.5)
393
+
394
+ ### Advisory vs Hard Mode
395
+ ```
396
+ Advisory mode (default): AI gets a warning, decides what to do
397
+ Hard mode: AI is BLOCKED — cannot proceed (MCP returns isError: true)
398
+ ```
399
+
400
+ - **Block threshold**: Configurable (default 70%). Only HIGH confidence conflicts block.
401
+ - **Override mechanism**: `speclock_override_lock` with a reason (logged to audit trail)
402
+ - **Escalation**: Lock overridden 3+ times → auto-creates a review note
403
+ - **Semantic pre-commit**: Parses actual git diff content, runs semantic analysis against locks
404
+
405
+ ## Security & Access Control (v3.0)
406
+
407
+ ### API Key Authentication
408
+ SHA-256 hashed keys stored server-side. HTTP transport uses `Authorization: Bearer <key>` headers. MCP transport authenticates via the `SPECLOCK_API_KEY` environment variable. Keys are never stored in plaintext.
409
+
410
+ ### RBAC (4 Roles)
411
+ | Role | Permissions |
412
+ |------|-------------|
413
+ | **viewer** | Read-only access to context, locks, decisions, and events |
414
+ | **developer** | Read + override locks with a documented reason |
415
+ | **architect** | Read + write (add/remove locks, decisions) + override |
416
+ | **admin** | Full access — manage keys, roles, enforcement settings, and all operations |
417
+
418
+ ### AES-256-GCM Encryption
419
+ Transparent encrypt-on-write / decrypt-on-read for `brain.json` and `events.log`. Encryption key is derived via PBKDF2 from the `SPECLOCK_ENCRYPTION_KEY` environment variable. Authenticated encryption (GCM) ensures both confidentiality and integrity. **HIPAA 2026 compliant.**
420
+
421
+ ### Test Coverage
422
+ **300 tests passing** (up from 186 in v2.5). Full coverage for authentication, authorization, encryption, semantic detection, and audit chain integrity.
423
+
375
424
  ---
376
425
 
377
426
  ## Architecture
@@ -382,14 +431,15 @@ Audits changed files against locks, posts PR comments, fails workflow on violati
382
431
  └──────────────┬──────────────────┬────────────────────┘
383
432
  │ │
384
433
  MCP Protocol File-Based (npm)
385
- (24 tool calls) (reads SPECLOCK.md +
434
+ (28 tool calls) (reads SPECLOCK.md +
386
435
  .speclock/context/latest.md,
387
436
  runs CLI commands)
388
437
  │ │
389
438
  ┌──────────────▼──────────────────▼────────────────────┐
390
439
  │ SpecLock Core Engine │
391
440
  │ Memory | Tracking | Enforcement | Git | Intelligence │
392
- │ Audit | Compliance | License
441
+ │ Audit | Compliance | License | Auth | RBAC
442
+ │ AES-256-GCM Encryption (brain.json, events.log) │
393
443
  └──────────────────────┬───────────────────────────────┘
394
444
 
395
445
  .speclock/
@@ -419,4 +469,4 @@ MIT License - see [LICENSE](LICENSE) file.
419
469
 
420
470
  ---
421
471
 
422
- *SpecLock v2.1.0 — Semantic conflict detection + enterprise audit & compliance. 100% detection, 0% false positives. HMAC audit chain, SOC 2/HIPAA exports. Because remembering isn't enough — AI needs to respect boundaries.*
472
+ *SpecLock v3.0.0 — Semantic conflict detection + enterprise audit & compliance. 100% detection, 0% false positives. HMAC audit chain, SOC 2/HIPAA exports. Hard enforcement mode. API Key Auth + RBAC. AES-256-GCM encrypted storage. 300 tests passing. Because remembering isn't enough — AI needs to respect boundaries.*
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "speclock",
3
- "version": "2.1.1",
4
- "description": "AI constraint engine with semantic conflict detection, HMAC audit chain, SOC 2/HIPAA compliance exports. 100% detection, 0% false positives. 24 MCP tools + CLI. Enterprise-ready memory + enforcement.",
3
+ "version": "3.0.0",
4
+ "description": "AI constraint engine with API key auth, RBAC, AES-256-GCM encryption, hard enforcement, semantic pre-commit, HMAC audit chain, SOC 2/HIPAA compliance. 100% detection, 0% false positives. 28 MCP tools + CLI. Enterprise-ready.",
5
5
  "type": "module",
6
6
  "main": "src/mcp/server.js",
7
7
  "bin": {
@@ -36,7 +36,12 @@
36
36
  "hipaa",
37
37
  "compliance",
38
38
  "audit-trail",
39
- "hmac"
39
+ "hmac",
40
+ "encryption",
41
+ "aes-256",
42
+ "api-key",
43
+ "authentication",
44
+ "rbac"
40
45
  ],
41
46
  "author": "Sandeep Roy (https://github.com/sgroy10)",
42
47
  "license": "MIT",
@@ -64,6 +69,7 @@
64
69
  "LICENSE"
65
70
  ],
66
71
  "devDependencies": {
67
- "esbuild": "^0.27.3"
72
+ "esbuild": "^0.27.3",
73
+ "jest": "^30.2.0"
68
74
  }
69
75
  }
package/src/cli/index.js CHANGED
@@ -23,10 +23,26 @@ import {
23
23
  verifyAuditChain,
24
24
  exportCompliance,
25
25
  getLicenseInfo,
26
+ enforceConflictCheck,
27
+ setEnforcementMode,
28
+ overrideLock,
29
+ getOverrideHistory,
30
+ getEnforcementConfig,
31
+ semanticAudit,
26
32
  } from "../core/engine.js";
27
33
  import { generateContext } from "../core/context.js";
28
34
  import { readBrain } from "../core/storage.js";
29
35
  import { installHook, removeHook } from "../core/hooks.js";
36
+ import {
37
+ isAuthEnabled,
38
+ enableAuth,
39
+ disableAuth,
40
+ createApiKey,
41
+ rotateApiKey,
42
+ revokeApiKey,
43
+ listApiKeys,
44
+ } from "../core/auth.js";
45
+ import { isEncryptionEnabled } from "../core/crypto.js";
30
46
 
31
47
  // --- Argument parsing ---
32
48
 
@@ -82,7 +98,7 @@ function refreshContext(root) {
82
98
 
83
99
  function printHelp() {
84
100
  console.log(`
85
- SpecLock v2.1.1 — AI Constraint Engine (Enterprise)
101
+ SpecLock v3.0.0 — AI Constraint Engine (Auth + RBAC + Encryption + Hard Enforcement)
86
102
  Developed by Sandeep Roy (github.com/sgroy10)
87
103
 
88
104
  Usage: speclock <command> [options]
@@ -105,7 +121,11 @@ Commands:
105
121
  hook install Install git pre-commit hook
106
122
  hook remove Remove git pre-commit hook
107
123
  audit Audit staged files against locks
124
+ audit-semantic Semantic audit: analyze code changes vs locks
108
125
  audit-verify Verify HMAC audit chain integrity
126
+ enforce <advisory|hard> Set enforcement mode (advisory=warn, hard=block)
127
+ override <lockId> <reason> Override a lock with justification
128
+ overrides [--lock <id>] Show override history
109
129
  export --format <soc2|hipaa|csv> Export compliance report
110
130
  license Show license tier and usage info
111
131
  context Generate and print context pack
@@ -126,10 +146,22 @@ Options:
126
146
 
127
147
  Templates: nextjs, react, express, supabase, stripe, security-hardened
128
148
 
149
+ Security (v3.0):
150
+ auth status Show auth status and active keys
151
+ auth create-key --role <role> Create API key (viewer/developer/architect/admin)
152
+ auth rotate-key <keyId> Rotate an API key
153
+ auth revoke-key <keyId> Revoke an API key
154
+ auth list-keys List all API keys
155
+ auth enable Enable API key authentication
156
+ auth disable Disable authentication
157
+ encrypt [status] Show encryption status
158
+
129
159
  Enterprise:
130
160
  SPECLOCK_AUDIT_SECRET HMAC secret for audit chain (env var)
131
161
  SPECLOCK_LICENSE_KEY License key for Pro/Enterprise features
132
162
  SPECLOCK_LLM_KEY API key for LLM-powered conflict detection
163
+ SPECLOCK_ENCRYPTION_KEY Master key for AES-256-GCM encryption
164
+ SPECLOCK_API_KEY API key for MCP server auth
133
165
 
134
166
  Examples:
135
167
  npx speclock setup --goal "Build PawPalace pet shop" --template nextjs
@@ -176,6 +208,8 @@ function showStatus(root) {
176
208
  }
177
209
 
178
210
  console.log(`Recent changes: ${brain.state.recentChanges.length}`);
211
+ console.log(`Auth: ${isAuthEnabled(root) ? "enabled" : "disabled"}`);
212
+ console.log(`Encryption: ${isEncryptionEnabled() ? "enabled (AES-256-GCM)" : "disabled"}`);
179
213
  console.log("");
180
214
  }
181
215
 
@@ -385,10 +419,12 @@ Tip: When starting a new chat, tell the AI:
385
419
  console.error('Usage: speclock check "what you plan to do"');
386
420
  process.exit(1);
387
421
  }
388
- const result = checkConflict(root, text);
422
+ const result = enforceConflictCheck(root, text);
389
423
  if (result.hasConflict) {
390
- console.log(`\nCONFLICT DETECTED`);
424
+ console.log(`\n${result.blocked ? "BLOCKED" : "CONFLICT DETECTED"}`);
391
425
  console.log("=".repeat(50));
426
+ console.log(`Mode: ${result.mode} | Threshold: ${result.threshold}%`);
427
+ console.log("");
392
428
  for (const lock of result.conflictingLocks) {
393
429
  console.log(` [${lock.level}] "${lock.text}"`);
394
430
  console.log(` Confidence: ${lock.confidence}%`);
@@ -400,6 +436,9 @@ Tip: When starting a new chat, tell the AI:
400
436
  console.log("");
401
437
  }
402
438
  console.log(result.analysis);
439
+ if (result.blocked) {
440
+ process.exit(1);
441
+ }
403
442
  } else {
404
443
  console.log(`No conflicts found. Safe to proceed with: "${text}"`);
405
444
  }
@@ -674,6 +713,211 @@ Tip: When starting a new chat, tell the AI:
674
713
  return;
675
714
  }
676
715
 
716
+ // --- ENFORCE (v2.5) ---
717
+ if (cmd === "enforce") {
718
+ const mode = args[0];
719
+ if (!mode || (mode !== "advisory" && mode !== "hard")) {
720
+ console.error("Usage: speclock enforce <advisory|hard> [--threshold 70]");
721
+ process.exit(1);
722
+ }
723
+ const flags = parseFlags(args.slice(1));
724
+ const options = {};
725
+ if (flags.threshold) options.blockThreshold = parseInt(flags.threshold, 10);
726
+ if (flags.override !== undefined) options.allowOverride = flags.override !== "false";
727
+ const result = setEnforcementMode(root, mode, options);
728
+ if (!result.success) {
729
+ console.error(result.error);
730
+ process.exit(1);
731
+ }
732
+ console.log(`\nEnforcement mode: ${result.mode.toUpperCase()}`);
733
+ console.log(`Block threshold: ${result.config.blockThreshold}%`);
734
+ console.log(`Overrides: ${result.config.allowOverride ? "allowed" : "disabled"}`);
735
+ if (result.mode === "hard") {
736
+ console.log(`\nHard mode active — conflicts above ${result.config.blockThreshold}% confidence will BLOCK actions.`);
737
+ }
738
+ return;
739
+ }
740
+
741
+ // --- OVERRIDE (v2.5) ---
742
+ if (cmd === "override") {
743
+ const lockId = args[0];
744
+ const reason = args.slice(1).join(" ");
745
+ if (!lockId || !reason) {
746
+ console.error("Usage: speclock override <lockId> <reason>");
747
+ process.exit(1);
748
+ }
749
+ const result = overrideLock(root, lockId, "(CLI override)", reason);
750
+ if (!result.success) {
751
+ console.error(result.error);
752
+ process.exit(1);
753
+ }
754
+ console.log(`Lock overridden: "${result.lockText}"`);
755
+ console.log(`Override count: ${result.overrideCount}`);
756
+ if (result.escalated) {
757
+ console.log(`\n${result.escalationMessage}`);
758
+ }
759
+ return;
760
+ }
761
+
762
+ // --- OVERRIDES (v2.5) ---
763
+ if (cmd === "overrides") {
764
+ const flags = parseFlags(args);
765
+ const result = getOverrideHistory(root, flags.lock || null);
766
+ if (result.total === 0) {
767
+ console.log("No overrides recorded.");
768
+ return;
769
+ }
770
+ console.log(`\nOverride History (${result.total})`);
771
+ console.log("=".repeat(50));
772
+ for (const o of result.overrides) {
773
+ console.log(`[${o.at.substring(0, 19)}] Lock: "${o.lockText}"`);
774
+ console.log(` Action: ${o.action}`);
775
+ console.log(` Reason: ${o.reason}`);
776
+ console.log("");
777
+ }
778
+ return;
779
+ }
780
+
781
+ // --- AUDIT-SEMANTIC (v2.5) ---
782
+ if (cmd === "audit-semantic") {
783
+ const result = semanticAudit(root);
784
+ console.log(`\nSemantic Pre-Commit Audit`);
785
+ console.log("=".repeat(50));
786
+ console.log(`Mode: ${result.mode} | Threshold: ${result.threshold}%`);
787
+ console.log(`Files analyzed: ${result.filesChecked}`);
788
+ console.log(`Active locks: ${result.activeLocks}`);
789
+ console.log(`Violations: ${result.violations.length}`);
790
+ if (result.violations.length > 0) {
791
+ console.log("");
792
+ for (const v of result.violations) {
793
+ console.log(` [${v.level}] ${v.file} (confidence: ${v.confidence}%)`);
794
+ console.log(` Lock: "${v.lockText}"`);
795
+ console.log(` Reason: ${v.reason}`);
796
+ if (v.addedLines !== undefined) {
797
+ console.log(` Changes: +${v.addedLines} / -${v.removedLines} lines`);
798
+ }
799
+ }
800
+ }
801
+ console.log(`\n${result.message}`);
802
+ process.exit(result.blocked ? 1 : 0);
803
+ }
804
+
805
+ // --- AUTH (v3.0) ---
806
+ if (cmd === "auth") {
807
+ const sub = args[0];
808
+ if (!sub || sub === "status") {
809
+ const enabled = isAuthEnabled(root);
810
+ console.log(`\nAuth Status: ${enabled ? "ENABLED" : "DISABLED"}`);
811
+ if (enabled) {
812
+ const keys = listApiKeys(root);
813
+ const active = keys.keys.filter(k => k.active);
814
+ console.log(`Active keys: ${active.length}`);
815
+ for (const k of active) {
816
+ console.log(` ${k.id} — ${k.name} (${k.role}) — last used: ${k.lastUsed || "never"}`);
817
+ }
818
+ } else {
819
+ console.log("Run 'speclock auth create-key --role admin' to enable auth.");
820
+ }
821
+ return;
822
+ }
823
+ if (sub === "create-key") {
824
+ const flags = parseFlags(args.slice(1));
825
+ const role = flags.role || "developer";
826
+ const name = flags.name || flags._.join(" ") || "";
827
+ const result = createApiKey(root, role, name);
828
+ if (!result.success) {
829
+ console.error(result.error);
830
+ process.exit(1);
831
+ }
832
+ console.log(`\nAPI Key Created`);
833
+ console.log("=".repeat(50));
834
+ console.log(`Key ID: ${result.keyId}`);
835
+ console.log(`Role: ${result.role}`);
836
+ console.log(`Name: ${result.name}`);
837
+ console.log(`\nRaw Key: ${result.rawKey}`);
838
+ console.log(`\nSave this key — it CANNOT be retrieved later.`);
839
+ console.log(`\nUsage:`);
840
+ console.log(` HTTP: Authorization: Bearer ${result.rawKey}`);
841
+ console.log(` MCP: Set SPECLOCK_API_KEY=${result.rawKey} in MCP config`);
842
+ return;
843
+ }
844
+ if (sub === "rotate-key") {
845
+ const keyId = args[1];
846
+ if (!keyId) {
847
+ console.error("Usage: speclock auth rotate-key <keyId>");
848
+ process.exit(1);
849
+ }
850
+ const result = rotateApiKey(root, keyId);
851
+ if (!result.success) {
852
+ console.error(result.error);
853
+ process.exit(1);
854
+ }
855
+ console.log(`\nKey Rotated`);
856
+ console.log(`Old key: ${result.oldKeyId} (revoked)`);
857
+ console.log(`New key: ${result.newKeyId}`);
858
+ console.log(`Raw Key: ${result.rawKey}`);
859
+ console.log(`\nSave this key — it CANNOT be retrieved later.`);
860
+ return;
861
+ }
862
+ if (sub === "revoke-key") {
863
+ const keyId = args[1];
864
+ if (!keyId) {
865
+ console.error("Usage: speclock auth revoke-key <keyId>");
866
+ process.exit(1);
867
+ }
868
+ const reason = args.slice(2).join(" ") || "manual";
869
+ const result = revokeApiKey(root, keyId, reason);
870
+ if (!result.success) {
871
+ console.error(result.error);
872
+ process.exit(1);
873
+ }
874
+ console.log(`Key revoked: ${result.keyId} (${result.name}, ${result.role})`);
875
+ return;
876
+ }
877
+ if (sub === "list-keys") {
878
+ const result = listApiKeys(root);
879
+ console.log(`\nAPI Keys (auth ${result.enabled ? "enabled" : "disabled"}):`);
880
+ console.log("=".repeat(50));
881
+ if (result.keys.length === 0) {
882
+ console.log(" No keys configured.");
883
+ } else {
884
+ for (const k of result.keys) {
885
+ const status = k.active ? "active" : `revoked (${k.revokedAt?.substring(0, 10) || "unknown"})`;
886
+ console.log(` ${k.id} — ${k.name} (${k.role}) [${status}]`);
887
+ }
888
+ }
889
+ return;
890
+ }
891
+ if (sub === "enable") {
892
+ enableAuth(root);
893
+ console.log("Auth enabled. API keys are now required for HTTP access.");
894
+ return;
895
+ }
896
+ if (sub === "disable") {
897
+ disableAuth(root);
898
+ console.log("Auth disabled. All operations allowed without keys.");
899
+ return;
900
+ }
901
+ console.error("Usage: speclock auth <create-key|rotate-key|revoke-key|list-keys|enable|disable|status>");
902
+ process.exit(1);
903
+ }
904
+
905
+ // --- ENCRYPT STATUS (v3.0) ---
906
+ if (cmd === "encrypt") {
907
+ const sub = args[0];
908
+ if (sub === "status" || !sub) {
909
+ const enabled = isEncryptionEnabled();
910
+ console.log(`\nEncryption: ${enabled ? "ENABLED (AES-256-GCM)" : "DISABLED"}`);
911
+ if (!enabled) {
912
+ console.log("Set SPECLOCK_ENCRYPTION_KEY env var to enable encryption.");
913
+ console.log("All data will be encrypted at rest (brain.json + events.log).");
914
+ }
915
+ return;
916
+ }
917
+ console.error("Usage: speclock encrypt [status]");
918
+ process.exit(1);
919
+ }
920
+
677
921
  // --- STATUS ---
678
922
  if (cmd === "status") {
679
923
  showStatus(root);