@vibecheckai/cli 3.2.5 → 3.3.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.
Files changed (197) hide show
  1. package/bin/.generated +25 -25
  2. package/bin/dev/run-v2-torture.js +30 -30
  3. package/bin/registry.js +192 -5
  4. package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
  5. package/bin/runners/lib/agent-firewall/change-packet/builder.js +280 -6
  6. package/bin/runners/lib/agent-firewall/critic/index.js +151 -0
  7. package/bin/runners/lib/agent-firewall/critic/judge.js +432 -0
  8. package/bin/runners/lib/agent-firewall/critic/prompts.js +305 -0
  9. package/bin/runners/lib/agent-firewall/lawbook/distributor.js +465 -0
  10. package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +604 -0
  11. package/bin/runners/lib/agent-firewall/lawbook/index.js +304 -0
  12. package/bin/runners/lib/agent-firewall/lawbook/registry.js +514 -0
  13. package/bin/runners/lib/agent-firewall/lawbook/schema.js +420 -0
  14. package/bin/runners/lib/agent-firewall/logger.js +141 -0
  15. package/bin/runners/lib/agent-firewall/policy/loader.js +312 -4
  16. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +113 -1
  17. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +133 -6
  18. package/bin/runners/lib/agent-firewall/proposal/extractor.js +394 -0
  19. package/bin/runners/lib/agent-firewall/proposal/index.js +212 -0
  20. package/bin/runners/lib/agent-firewall/proposal/schema.js +251 -0
  21. package/bin/runners/lib/agent-firewall/proposal/validator.js +386 -0
  22. package/bin/runners/lib/agent-firewall/reality/index.js +332 -0
  23. package/bin/runners/lib/agent-firewall/reality/state.js +625 -0
  24. package/bin/runners/lib/agent-firewall/reality/watcher.js +322 -0
  25. package/bin/runners/lib/agent-firewall/risk/index.js +173 -0
  26. package/bin/runners/lib/agent-firewall/risk/scorer.js +328 -0
  27. package/bin/runners/lib/agent-firewall/risk/thresholds.js +321 -0
  28. package/bin/runners/lib/agent-firewall/risk/vectors.js +421 -0
  29. package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +472 -0
  30. package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +346 -0
  31. package/bin/runners/lib/agent-firewall/simulator/index.js +181 -0
  32. package/bin/runners/lib/agent-firewall/simulator/route-validator.js +380 -0
  33. package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +661 -0
  34. package/bin/runners/lib/agent-firewall/time-machine/index.js +267 -0
  35. package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +436 -0
  36. package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +490 -0
  37. package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +530 -0
  38. package/bin/runners/lib/analyzers.js +81 -18
  39. package/bin/runners/lib/api-client.js +269 -0
  40. package/bin/runners/lib/auth-truth.js +193 -193
  41. package/bin/runners/lib/authority-badge.js +425 -0
  42. package/bin/runners/lib/backup.js +62 -62
  43. package/bin/runners/lib/billing.js +107 -107
  44. package/bin/runners/lib/claims.js +118 -118
  45. package/bin/runners/lib/cli-output.js +7 -1
  46. package/bin/runners/lib/cli-ui.js +540 -540
  47. package/bin/runners/lib/contracts/auth-contract.js +202 -202
  48. package/bin/runners/lib/contracts/env-contract.js +181 -181
  49. package/bin/runners/lib/contracts/external-contract.js +206 -206
  50. package/bin/runners/lib/contracts/guard.js +168 -168
  51. package/bin/runners/lib/contracts/index.js +89 -89
  52. package/bin/runners/lib/contracts/plan-validator.js +311 -311
  53. package/bin/runners/lib/contracts/route-contract.js +199 -199
  54. package/bin/runners/lib/contracts.js +804 -804
  55. package/bin/runners/lib/detect.js +89 -89
  56. package/bin/runners/lib/doctor/autofix.js +254 -254
  57. package/bin/runners/lib/doctor/index.js +37 -37
  58. package/bin/runners/lib/doctor/modules/dependencies.js +325 -325
  59. package/bin/runners/lib/doctor/modules/index.js +46 -46
  60. package/bin/runners/lib/doctor/modules/network.js +250 -250
  61. package/bin/runners/lib/doctor/modules/project.js +312 -312
  62. package/bin/runners/lib/doctor/modules/runtime.js +224 -224
  63. package/bin/runners/lib/doctor/modules/security.js +348 -348
  64. package/bin/runners/lib/doctor/modules/system.js +213 -213
  65. package/bin/runners/lib/doctor/modules/vibecheck.js +394 -394
  66. package/bin/runners/lib/doctor/reporter.js +262 -262
  67. package/bin/runners/lib/doctor/service.js +262 -262
  68. package/bin/runners/lib/doctor/types.js +113 -113
  69. package/bin/runners/lib/doctor/ui.js +263 -263
  70. package/bin/runners/lib/doctor-v2.js +608 -608
  71. package/bin/runners/lib/drift.js +425 -425
  72. package/bin/runners/lib/enforcement.js +72 -72
  73. package/bin/runners/lib/enterprise-detect.js +603 -603
  74. package/bin/runners/lib/enterprise-init.js +942 -942
  75. package/bin/runners/lib/env-resolver.js +417 -417
  76. package/bin/runners/lib/env-template.js +66 -66
  77. package/bin/runners/lib/env.js +189 -189
  78. package/bin/runners/lib/error-handler.js +16 -9
  79. package/bin/runners/lib/exit-codes.js +275 -0
  80. package/bin/runners/lib/extractors/client-calls.js +990 -990
  81. package/bin/runners/lib/extractors/fastify-route-dump.js +573 -573
  82. package/bin/runners/lib/extractors/fastify-routes.js +426 -426
  83. package/bin/runners/lib/extractors/index.js +363 -363
  84. package/bin/runners/lib/extractors/next-routes.js +524 -524
  85. package/bin/runners/lib/extractors/proof-graph.js +431 -431
  86. package/bin/runners/lib/extractors/route-matcher.js +451 -451
  87. package/bin/runners/lib/extractors/truthpack-v2.js +377 -377
  88. package/bin/runners/lib/extractors/ui-bindings.js +547 -547
  89. package/bin/runners/lib/findings-schema.js +281 -281
  90. package/bin/runners/lib/firewall-prompt.js +50 -50
  91. package/bin/runners/lib/global-flags.js +37 -0
  92. package/bin/runners/lib/graph/graph-builder.js +265 -265
  93. package/bin/runners/lib/graph/html-renderer.js +413 -413
  94. package/bin/runners/lib/graph/index.js +32 -32
  95. package/bin/runners/lib/graph/runtime-collector.js +215 -215
  96. package/bin/runners/lib/graph/static-extractor.js +518 -518
  97. package/bin/runners/lib/help-formatter.js +413 -0
  98. package/bin/runners/lib/html-report.js +650 -650
  99. package/bin/runners/lib/llm.js +75 -75
  100. package/bin/runners/lib/logger.js +38 -0
  101. package/bin/runners/lib/meter.js +61 -61
  102. package/bin/runners/lib/missions/evidence.js +126 -126
  103. package/bin/runners/lib/patch.js +40 -40
  104. package/bin/runners/lib/permissions/auth-model.js +213 -213
  105. package/bin/runners/lib/permissions/idor-prover.js +205 -205
  106. package/bin/runners/lib/permissions/index.js +45 -45
  107. package/bin/runners/lib/permissions/matrix-builder.js +198 -198
  108. package/bin/runners/lib/pkgjson.js +28 -28
  109. package/bin/runners/lib/policy.js +295 -295
  110. package/bin/runners/lib/preflight.js +142 -142
  111. package/bin/runners/lib/reality/correlation-detectors.js +359 -359
  112. package/bin/runners/lib/reality/index.js +318 -318
  113. package/bin/runners/lib/reality/request-hashing.js +416 -416
  114. package/bin/runners/lib/reality/request-mapper.js +453 -453
  115. package/bin/runners/lib/reality/safety-rails.js +463 -463
  116. package/bin/runners/lib/reality/semantic-snapshot.js +408 -408
  117. package/bin/runners/lib/reality/toast-detector.js +393 -393
  118. package/bin/runners/lib/reality-findings.js +84 -84
  119. package/bin/runners/lib/receipts.js +179 -179
  120. package/bin/runners/lib/redact.js +29 -29
  121. package/bin/runners/lib/replay/capsule-manager.js +154 -154
  122. package/bin/runners/lib/replay/index.js +263 -263
  123. package/bin/runners/lib/replay/player.js +348 -348
  124. package/bin/runners/lib/replay/recorder.js +331 -331
  125. package/bin/runners/lib/report.js +135 -135
  126. package/bin/runners/lib/route-detection.js +1140 -1140
  127. package/bin/runners/lib/sandbox/index.js +59 -59
  128. package/bin/runners/lib/sandbox/proof-chain.js +399 -399
  129. package/bin/runners/lib/sandbox/sandbox-runner.js +205 -205
  130. package/bin/runners/lib/sandbox/worktree.js +174 -174
  131. package/bin/runners/lib/schema-validator.js +350 -350
  132. package/bin/runners/lib/schemas/contracts.schema.json +160 -160
  133. package/bin/runners/lib/schemas/finding.schema.json +100 -100
  134. package/bin/runners/lib/schemas/mission-pack.schema.json +206 -206
  135. package/bin/runners/lib/schemas/proof-graph.schema.json +176 -176
  136. package/bin/runners/lib/schemas/reality-report.schema.json +162 -162
  137. package/bin/runners/lib/schemas/share-pack.schema.json +180 -180
  138. package/bin/runners/lib/schemas/ship-report.schema.json +117 -117
  139. package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -303
  140. package/bin/runners/lib/schemas/validator.js +438 -438
  141. package/bin/runners/lib/score-history.js +282 -282
  142. package/bin/runners/lib/share-pack.js +239 -239
  143. package/bin/runners/lib/snippets.js +67 -67
  144. package/bin/runners/lib/unified-cli-output.js +604 -0
  145. package/bin/runners/lib/upsell.js +658 -510
  146. package/bin/runners/lib/usage.js +153 -153
  147. package/bin/runners/lib/validate-patch.js +156 -156
  148. package/bin/runners/lib/verdict-engine.js +628 -628
  149. package/bin/runners/reality/engine.js +917 -917
  150. package/bin/runners/reality/flows.js +122 -122
  151. package/bin/runners/reality/report.js +378 -378
  152. package/bin/runners/reality/session.js +193 -193
  153. package/bin/runners/runAgent.d.ts +5 -0
  154. package/bin/runners/runApprove.js +1200 -0
  155. package/bin/runners/runAuth.js +324 -95
  156. package/bin/runners/runCheckpoint.js +39 -21
  157. package/bin/runners/runClassify.js +859 -0
  158. package/bin/runners/runContext.js +136 -24
  159. package/bin/runners/runDoctor.js +108 -68
  160. package/bin/runners/runFirewall.d.ts +5 -0
  161. package/bin/runners/runFirewallHook.d.ts +5 -0
  162. package/bin/runners/runFix.js +6 -5
  163. package/bin/runners/runGuard.js +262 -168
  164. package/bin/runners/runInit.js +3 -2
  165. package/bin/runners/runMcp.js +130 -52
  166. package/bin/runners/runPolish.js +43 -20
  167. package/bin/runners/runProve.js +1 -2
  168. package/bin/runners/runReport.js +3 -2
  169. package/bin/runners/runScan.js +145 -44
  170. package/bin/runners/runShip.js +3 -4
  171. package/bin/runners/runTruth.d.ts +5 -0
  172. package/bin/runners/runValidate.js +19 -2
  173. package/bin/runners/runWatch.js +104 -53
  174. package/bin/vibecheck.js +106 -19
  175. package/mcp-server/HARDENING_SUMMARY.md +299 -0
  176. package/mcp-server/agent-firewall-interceptor.js +367 -31
  177. package/mcp-server/authority-tools.js +569 -0
  178. package/mcp-server/conductor/conflict-resolver.js +588 -0
  179. package/mcp-server/conductor/execution-planner.js +544 -0
  180. package/mcp-server/conductor/index.js +377 -0
  181. package/mcp-server/conductor/lock-manager.js +615 -0
  182. package/mcp-server/conductor/request-queue.js +550 -0
  183. package/mcp-server/conductor/session-manager.js +500 -0
  184. package/mcp-server/conductor/tools.js +510 -0
  185. package/mcp-server/index.js +1199 -208
  186. package/mcp-server/lib/api-client.cjs +305 -0
  187. package/mcp-server/lib/logger.cjs +30 -0
  188. package/mcp-server/logger.js +173 -0
  189. package/mcp-server/package.json +2 -2
  190. package/mcp-server/premium-tools.js +2 -2
  191. package/mcp-server/tier-auth.js +351 -136
  192. package/mcp-server/tools/index.js +72 -72
  193. package/mcp-server/truth-firewall-tools.js +145 -15
  194. package/mcp-server/vibecheck-tools.js +2 -2
  195. package/package.json +2 -3
  196. package/mcp-server/index.old.js +0 -4137
  197. package/mcp-server/package-lock.json +0 -165
@@ -1,157 +1,276 @@
1
+ /* vibecheck-disable */
1
2
  /**
2
3
  * MCP Server Tier Authentication & Authorization
3
4
  *
4
- * Provides tier checking for MCP tools based on API keys
5
+ * UNIFIED AUTHORITY SYSTEM for MCP tools.
6
+ *
7
+ * Authority Rules:
8
+ * - FREE: May see problems, never resolve certainty. Read-only context tools.
9
+ * - STARTER: May fix, but not prove. Read tools + advisory actions.
10
+ * - PRO: May enforce reality. Full MCP access with proof generation.
11
+ * - ENTERPRISE: All features + compliance tools.
12
+ *
13
+ * Verdict authority is paid. Evidence beats explanations.
5
14
  */
6
15
 
7
16
  import fs from "fs/promises";
8
17
  import path from "path";
9
18
  import os from "os";
10
19
 
11
- // Tier definitions - MUST MATCH CLI entitlements-v2.js
20
+ // ═══════════════════════════════════════════════════════════════════════════════
21
+ // TIER DEFINITIONS - UNIFIED AUTHORITY SYSTEM
22
+ // Synchronized with packages/core/src/tier-config.ts
23
+ //
24
+ // Authority Rules:
25
+ // - FREE: May see problems, never resolve certainty
26
+ // - STARTER: May fix, but not prove (Advisory verdicts only)
27
+ // - PRO: May enforce reality (Full verdict authority)
28
+ // ═══════════════════════════════════════════════════════════════════════════════
12
29
  export const TIERS = {
13
30
  free: {
14
31
  name: 'FREE',
15
32
  price: 0,
16
33
  order: 0,
17
- features: [
18
- // Core commands
19
- 'scan', 'ship', 'ship.static',
20
- // Setup
21
- 'init', 'init.local', 'doctor', 'status', 'install', 'preflight', 'watch', 'watch.local',
22
- // AI Truth
23
- 'ctx', 'guard', 'context', 'mdc', 'contracts',
24
- // Quality
25
- 'verify', 'quality', 'polish', 'checkpoint', 'checkpoint.basic',
26
- // Fix (plan only)
27
- 'fix', 'fix.plan_only',
28
- // Reality (preview)
29
- 'reality', 'reality.preview',
30
- // MCP (help only)
31
- 'mcp.help_only',
32
- // Report (html/md only)
33
- 'report', 'report.html_md',
34
- ],
34
+ // Verdict authority
35
+ verdictAuthority: 'NONE',
36
+ canIssueVerdicts: false,
37
+ canEnforceCI: false,
38
+ canGenerateProof: false,
39
+ // Agent Firewall configuration
40
+ firewall: {
41
+ mode: 'observe', // Log only, never block
42
+ canOverride: false, // Cannot override blocks
43
+ auditEnabled: false, // No audit trail
44
+ },
45
+ // Limits
35
46
  limits: {
36
- scans: 50,
37
- shipChecks: 20,
47
+ scans: -1, // Unlimited scans
38
48
  realityMaxPages: 5,
39
49
  realityMaxClicks: 20,
40
- fixApplyPatches: false,
41
- mcpRateLimit: 10, // requests per minute
50
+ mcpRateLimit: 10,
42
51
  },
43
- // MCP tools allowed on FREE
52
+ // MCP tools allowed on FREE (read-only context only)
44
53
  mcpTools: [
45
54
  'vibecheck.get_truthpack',
46
- 'vibecheck.validate_claim',
47
55
  'vibecheck.compile_context',
48
56
  'vibecheck.search_evidence',
57
+ 'vibecheck_agent_firewall_intercept', // Observe mode
58
+ 'vibecheck_conductor_status', // Read-only coordination status
59
+ // Authority System - FREE tier
60
+ 'vibecheck.classify', // Inventory authority (read-only)
61
+ 'vibecheck.authority_list', // List available authorities
49
62
  ],
63
+ // Authority System configuration
64
+ authority: {
65
+ canClassify: true, // Can run inventory analysis
66
+ canApprove: false, // Cannot get verdicts
67
+ canEnforce: false, // Cannot enforce in CI
68
+ canGenerateBadge: false, // Cannot generate badges
69
+ availableAuthorities: ['inventory'],
70
+ },
50
71
  },
51
72
  starter: {
52
73
  name: 'STARTER',
53
- price: 39, // Updated pricing
74
+ price: 39,
54
75
  order: 1,
55
- features: [
56
- // All FREE features plus...
57
- // Init connect
58
- 'init.connect',
59
- // Scan autofix
60
- 'scan.autofix',
61
- // CI/CD
62
- 'gate', 'pr', 'badge', 'launch', 'dashboard_sync',
63
- // Watch PR
64
- 'watch.pr',
65
- // Report formats
66
- 'report.sarif_csv',
67
- // Reality basic
68
- 'reality.basic',
69
- // MCP read-only
70
- 'mcp', 'mcp.read_only',
71
- // Ship full
72
- 'ship.full',
73
- ],
76
+ // Verdict authority - ADVISORY (verdicts not enforced)
77
+ verdictAuthority: 'ADVISORY',
78
+ canIssueVerdicts: true, // SHIP, WARN only
79
+ canEnforceCI: false, // Cannot block builds
80
+ canGenerateProof: false, // Cannot generate proof
81
+ // Agent Firewall configuration
82
+ firewall: {
83
+ mode: 'advisory', // Warn but allow
84
+ canOverride: true, // Can override with reason
85
+ auditEnabled: true, // Basic audit trail
86
+ },
87
+ // Limits
74
88
  limits: {
75
- scans: -1,
76
- shipChecks: -1,
77
- realityMaxPages: 50,
78
- realityMaxClicks: 200,
79
- fixApplyPatches: false,
80
- mcpRateLimit: 60, // requests per minute
89
+ scans: -1, // Unlimited scans
90
+ realityMaxPages: -1, // Unlimited browser testing
91
+ realityMaxClicks: -1,
92
+ mcpRateLimit: 60,
81
93
  },
82
- // MCP tools allowed on STARTER (curated)
94
+ // MCP tools allowed on STARTER (read + advisory actions)
83
95
  mcpTools: [
84
96
  'vibecheck.ctx',
85
97
  'vibecheck.scan',
86
- 'vibecheck.ship',
98
+ 'vibecheck.ship', // Advisory verdicts only
87
99
  'vibecheck.get_truthpack',
88
100
  'vibecheck.validate_claim',
89
101
  'vibecheck.compile_context',
90
102
  'vibecheck.search_evidence',
91
103
  'vibecheck.find_counterexamples',
92
104
  'vibecheck.check_invariants',
105
+ 'vibecheck.fix', // Plan mode only
106
+ 'vibecheck_agent_firewall_intercept', // Advisory mode
107
+ // Conductor - multi-agent coordination
108
+ 'vibecheck_conductor_register',
109
+ 'vibecheck_conductor_acquire_lock',
110
+ 'vibecheck_conductor_release_lock',
111
+ 'vibecheck_conductor_propose',
112
+ 'vibecheck_conductor_status',
113
+ 'vibecheck_conductor_terminate',
114
+ // Authority System - STARTER tier
115
+ 'vibecheck.classify', // Inventory authority
116
+ 'vibecheck.approve', // Advisory verdicts
117
+ 'vibecheck.authority_list', // List authorities
118
+ 'vibecheck.authority_approve', // Authority approval (advisory)
93
119
  ],
120
+ // Authority System configuration
121
+ authority: {
122
+ canClassify: true, // Can run inventory analysis
123
+ canApprove: true, // Can get advisory verdicts
124
+ canEnforce: false, // Cannot enforce in CI
125
+ canGenerateBadge: false, // Cannot generate badges
126
+ availableAuthorities: ['inventory', 'safe-consolidation'],
127
+ },
94
128
  },
95
129
  pro: {
96
130
  name: 'PRO',
97
131
  price: 99,
98
132
  order: 2,
99
- features: [
100
- // All STARTER features plus...
101
- // Prove
102
- 'prove',
103
- // Fix apply
104
- 'fix.apply_patches', 'fix.loop',
105
- // Checkpoint advanced
106
- 'checkpoint.hallucination',
107
- // Reality advanced
108
- 'reality.full', 'reality.advanced_auth_boundary',
109
- // Premium
110
- 'replay', 'share', 'ai-test', 'permissions', 'graph',
111
- // MCP full
112
- 'mcp.full',
113
- ],
133
+ // Verdict authority - ENFORCED (verdicts block CI/PRs)
134
+ verdictAuthority: 'ENFORCED',
135
+ canIssueVerdicts: true, // SHIP, WARN, BLOCK
136
+ canEnforceCI: true, // Can block builds
137
+ canGenerateProof: true, // Can generate cryptographic proof
138
+ // Agent Firewall configuration
139
+ firewall: {
140
+ mode: 'enforce', // Block violations
141
+ canOverride: true, // Can override with approval
142
+ auditEnabled: true, // Full audit trail
143
+ criticEnabled: true, // Enable Critic LLM
144
+ },
145
+ // Limits
114
146
  limits: {
115
- scans: -1,
116
- shipChecks: -1,
117
- realityMaxPages: -1,
118
- realityMaxClicks: -1,
119
- fixApplyPatches: true,
120
- mcpRateLimit: -1, // unlimited
147
+ scans: -1,
148
+ realityMaxPages: -1,
149
+ realityMaxClicks: -1,
150
+ mcpRateLimit: -1, // Unlimited
151
+ },
152
+ // MCP tools - FULL ACCESS
153
+ mcpTools: ['*'], // All tools unlocked
154
+ // Authority System configuration
155
+ authority: {
156
+ canClassify: true, // Can run inventory analysis
157
+ canApprove: true, // Can get verdicts
158
+ canEnforce: true, // Can enforce in CI (STOP verdicts block)
159
+ canGenerateBadge: true, // Can generate authority badges
160
+ availableAuthorities: ['inventory', 'safe-consolidation', 'security-remediation'],
121
161
  },
122
- // MCP tools allowed on PRO (curated)
123
- mcpTools: [
124
- 'vibecheck.ctx',
125
- 'vibecheck.scan',
126
- 'vibecheck.ship',
127
- 'vibecheck.get_truthpack',
128
- 'vibecheck.validate_claim',
129
- 'vibecheck.compile_context',
130
- 'vibecheck.search_evidence',
131
- 'vibecheck.find_counterexamples',
132
- 'vibecheck.check_invariants',
133
- ],
134
162
  },
135
- compliance: {
136
- name: 'COMPLIANCE',
137
- price: 0, // Enterprise/on-prem
163
+ enterprise: {
164
+ name: 'ENTERPRISE',
165
+ price: 0, // Custom pricing
138
166
  order: 3,
139
- features: [
140
- // All PRO features plus...
141
- 'report.compliance_packs',
142
- ],
167
+ // Verdict authority - ENFORCED + Compliance
168
+ verdictAuthority: 'ENFORCED',
169
+ canIssueVerdicts: true,
170
+ canEnforceCI: true,
171
+ canGenerateProof: true,
172
+ // Agent Firewall configuration
173
+ firewall: {
174
+ mode: 'enforce', // Block violations
175
+ canOverride: true, // Can override with multi-approval
176
+ auditEnabled: true, // Full compliance audit trail
177
+ criticEnabled: true, // Enable Critic LLM
178
+ complianceMode: true, // Generate compliance artifacts
179
+ },
180
+ // Limits
143
181
  limits: {
144
- scans: -1,
145
- shipChecks: -1,
146
- realityMaxPages: -1,
147
- realityMaxClicks: -1,
148
- fixApplyPatches: true,
182
+ scans: -1,
183
+ realityMaxPages: -1,
184
+ realityMaxClicks: -1,
149
185
  mcpRateLimit: -1,
150
186
  },
151
- mcpTools: ['*'], // All tools
187
+ // MCP tools - FULL ACCESS + Compliance
188
+ mcpTools: ['*'],
189
+ // Authority System configuration
190
+ authority: {
191
+ canClassify: true, // Can run inventory analysis
192
+ canApprove: true, // Can get verdicts
193
+ canEnforce: true, // Can enforce in CI (STOP verdicts block)
194
+ canGenerateBadge: true, // Can generate authority badges
195
+ canCustomize: true, // Can create custom authorities
196
+ availableAuthorities: ['*'], // All authorities including custom
197
+ },
152
198
  }
153
199
  };
154
200
 
201
+ // ═══════════════════════════════════════════════════════════════════════════════
202
+ // CLI FEATURE → TIER MAPPING
203
+ // Maps CLI features (not MCP tools) to minimum required tier
204
+ // ═══════════════════════════════════════════════════════════════════════════════
205
+ export const CLI_FEATURE_TIERS = {
206
+ // FREE - Basic scanning (always available)
207
+ 'scan': 'free',
208
+ 'ctx': 'free',
209
+ 'ship': 'free',
210
+ 'classify': 'free', // Authority: inventory (read-only)
211
+
212
+ // STARTER - Enhanced features
213
+ 'gate': 'starter',
214
+ 'badge': 'starter',
215
+ 'fix': 'starter', // Plan mode
216
+ 'approve': 'starter', // Authority: advisory verdicts
217
+ 'authority.starter': 'starter', // Authority System access
218
+
219
+ // PRO - Full enforcement
220
+ 'prove': 'pro',
221
+ 'fix.apply_patches': 'pro', // Apply mode
222
+ 'reality': 'pro',
223
+ 'autopilot': 'pro',
224
+ 'verify': 'pro',
225
+ 'authority.pro': 'pro', // Authority: enforced verdicts + badges
226
+ 'authority.enforce': 'pro', // Authority: CI blocking
227
+ };
228
+
229
+ // ═══════════════════════════════════════════════════════════════════════════════
230
+ // MCP TOOL → TIER MAPPING (Unified Authority System)
231
+ // Aligned with packages/core/src/features.ts
232
+ // ═══════════════════════════════════════════════════════════════════════════════
233
+ export const MCP_TOOL_TIERS = {
234
+ // FREE - Read-only context tools ONLY
235
+ // Free users may see problems, never resolve certainty
236
+ 'vibecheck.get_truthpack': 'free',
237
+ 'vibecheck.compile_context': 'free',
238
+ 'vibecheck.search_evidence': 'free',
239
+ 'vibecheck_agent_firewall_intercept': 'free', // Observe mode only
240
+ 'vibecheck.classify': 'free', // Authority: inventory (read-only)
241
+ 'vibecheck.authority_list': 'free', // List available authorities
242
+
243
+ // STARTER - Advisory verdict authority
244
+ // Starter users may fix, but not prove
245
+ 'vibecheck.ctx': 'starter',
246
+ 'vibecheck.scan': 'starter',
247
+ 'vibecheck.ship': 'starter', // SHIP/WARN (advisory, not enforced)
248
+ 'vibecheck.fix': 'starter', // Plan mode only (no apply)
249
+ 'vibecheck.validate_claim': 'starter',
250
+ 'vibecheck.find_counterexamples': 'starter',
251
+ 'vibecheck.check_invariants': 'starter',
252
+ 'vibecheck.gate': 'starter', // Advisory gates
253
+ 'vibecheck.badge': 'starter', // Unverified badges
254
+ 'vibecheck.approve': 'starter', // Authority: advisory verdicts
255
+ 'vibecheck.authority_approve': 'starter', // Authority approval (advisory)
256
+
257
+ // PRO - Full verdict authority & enforcement
258
+ // Pro users may enforce reality
259
+ 'vibecheck.prove': 'pro', // Cryptographic proof generation
260
+ 'vibecheck.fix.apply': 'pro', // Apply fixes
261
+ 'vibecheck.fix.verify': 'pro', // Verify fixes with proof
262
+ 'vibecheck.reality': 'pro', // Full browser testing
263
+ 'vibecheck.reality.prove': 'pro', // Prove runtime behavior
264
+ 'vibecheck.enforce': 'pro', // Enforce verdicts in CI
265
+ 'vibecheck.ai_test': 'pro', // AI agent testing
266
+ 'vibecheck.autopilot': 'pro', // Autonomous protection
267
+ 'vibecheck.report': 'pro', // Advanced reporting
268
+ 'vibecheck.allowlist': 'pro', // Manage allowlists
269
+ 'vibecheck.status': 'pro', // Advanced status
270
+ 'vibecheck.authority_enforce': 'pro', // Authority: enforced verdicts
271
+ 'vibecheck.authority_badge': 'pro', // Authority: generate badges
272
+ };
273
+
155
274
  /**
156
275
  * Load user configuration from ~/.vibecheck/credentials.json
157
276
  */
@@ -165,50 +284,149 @@ async function loadUserConfig() {
165
284
  }
166
285
  }
167
286
 
287
+ // ═══════════════════════════════════════════════════════════════════════════════
288
+ // SECURE TIER VALIDATION - API-First with Caching
289
+ // SECURITY: Never trust API key prefix alone - always validate with API
290
+ // ═══════════════════════════════════════════════════════════════════════════════
291
+
168
292
  /**
169
- * Determine tier from API key
170
- * Matches CLI entitlements-v2.js logic
293
+ * Tier cache to avoid hammering the API on every request
294
+ * Structure: Map<apiKeyHash, { tier, validatedAt, expiresAt }>
295
+ */
296
+ const tierCache = new Map();
297
+ const TIER_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
298
+ const TIER_CACHE_MAX_SIZE = 1000; // Prevent unbounded growth
299
+
300
+ /**
301
+ * Hash API key for cache key (don't store raw keys in memory)
302
+ */
303
+ function hashApiKey(apiKey) {
304
+ const crypto = require('crypto');
305
+ return crypto.createHash('sha256').update(apiKey).digest('hex').slice(0, 32);
306
+ }
307
+
308
+ /**
309
+ * Evict expired entries and enforce max size
310
+ */
311
+ function cleanupTierCache() {
312
+ const now = Date.now();
313
+ for (const [key, value] of tierCache) {
314
+ if (value.expiresAt < now) {
315
+ tierCache.delete(key);
316
+ }
317
+ }
318
+ // If still too large, evict oldest entries
319
+ if (tierCache.size > TIER_CACHE_MAX_SIZE) {
320
+ const entries = Array.from(tierCache.entries())
321
+ .sort((a, b) => a[1].validatedAt - b[1].validatedAt);
322
+ const toDelete = entries.slice(0, tierCache.size - TIER_CACHE_MAX_SIZE);
323
+ for (const [key] of toDelete) {
324
+ tierCache.delete(key);
325
+ }
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Determine tier from API key - ALWAYS validates with API first
331
+ *
332
+ * SECURITY FIX: Previous version checked prefix patterns BEFORE API validation,
333
+ * allowing attackers to spoof tier with fake keys like "gr_pro_anything".
334
+ * Now we ALWAYS validate with API first. Prefix is never authoritative.
335
+ *
336
+ * @param {string} apiKey - The API key to validate
337
+ * @returns {Promise<string|null>} - Tier name or null if invalid
171
338
  */
172
339
  async function getTierFromApiKey(apiKey) {
173
- if (!apiKey) return null; // No API key = no access
340
+ if (!apiKey || typeof apiKey !== 'string') return null;
341
+
342
+ // Validate key format (basic sanity check)
343
+ if (apiKey.length < 10 || apiKey.length > 256) return null;
174
344
 
175
- // Check API key prefix patterns (matches CLI)
176
- if (apiKey.startsWith('gr_starter_')) return 'starter';
177
- if (apiKey.startsWith('gr_pro_')) return 'pro';
178
- if (apiKey.startsWith('gr_compliance_') || apiKey.startsWith('gr_ent_')) return 'compliance';
179
- if (apiKey.startsWith('gr_free_')) return 'free';
345
+ const keyHash = hashApiKey(apiKey);
346
+ const now = Date.now();
347
+
348
+ // Check cache first (only if not expired)
349
+ const cached = tierCache.get(keyHash);
350
+ if (cached && cached.expiresAt > now) {
351
+ return cached.tier;
352
+ }
180
353
 
181
- // Try to validate with API
354
+ // ALWAYS validate with API - this is the authoritative source
182
355
  try {
356
+ const controller = new AbortController();
357
+ const timeoutId = setTimeout(() => controller.abort(), 10000); // 10s timeout
358
+
183
359
  const response = await fetch('https://api.vibecheckai.dev/whoami', {
184
360
  headers: {
185
361
  'Authorization': `Bearer ${apiKey}`,
186
362
  'Content-Type': 'application/json',
187
363
  },
364
+ signal: controller.signal,
188
365
  });
189
366
 
367
+ clearTimeout(timeoutId);
368
+
190
369
  if (!response.ok) {
191
- return null; // Invalid API key
370
+ // API explicitly rejected this key - it's invalid
371
+ // Cache the rejection briefly to avoid hammering on invalid keys
372
+ tierCache.set(keyHash, {
373
+ tier: null,
374
+ validatedAt: now,
375
+ expiresAt: now + 60000, // Cache rejections for 1 minute
376
+ });
377
+ return null;
192
378
  }
193
379
 
194
380
  const data = await response.json();
195
381
 
196
382
  // Map API response to tier
383
+ let tier;
197
384
  switch (data.plan?.toLowerCase()) {
198
385
  case 'starter':
199
- return 'starter';
386
+ tier = 'starter';
387
+ break;
200
388
  case 'pro':
201
- return 'pro';
202
- case 'compliance':
203
- return 'compliance';
389
+ tier = 'pro';
390
+ break;
391
+ case 'enterprise':
392
+ tier = 'enterprise';
393
+ break;
204
394
  default:
205
- return 'free';
395
+ tier = 'free';
206
396
  }
397
+
398
+ // Cache the validated tier
399
+ tierCache.set(keyHash, {
400
+ tier,
401
+ validatedAt: now,
402
+ expiresAt: now + TIER_CACHE_TTL_MS,
403
+ });
404
+
405
+ // Periodic cleanup
406
+ if (tierCache.size > TIER_CACHE_MAX_SIZE * 0.9) {
407
+ cleanupTierCache();
408
+ }
409
+
410
+ return tier;
411
+
207
412
  } catch (error) {
208
- console.error('API validation failed:', error);
209
- return null; // On error, deny access
413
+ // Network error - check if we have a recent valid cache entry
414
+ // (This allows graceful degradation during brief network issues)
415
+ if (cached && cached.tier !== null) {
416
+ // Only use stale cache if it was validated within last 15 minutes
417
+ // and the original validation was successful (tier !== null)
418
+ const staleTolerance = 15 * 60 * 1000; // 15 minutes
419
+ if (now - cached.validatedAt < staleTolerance) {
420
+ console.error(`[TIER-AUTH] API unreachable, using stale cache for ${keyHash.slice(0, 8)}...`);
421
+ return cached.tier;
422
+ }
423
+ }
424
+
425
+ // No valid cache - fail closed for security
426
+ console.error('[TIER-AUTH] API validation failed with no valid cache, denying access:', error.message);
427
+ return null;
210
428
  }
211
- } // default for unknown keys
429
+ }
212
430
 
213
431
  /**
214
432
  * Check if user has access to a specific feature
@@ -240,28 +458,20 @@ export async function getFeatureAccessStatus(featureName, providedApiKey = null)
240
458
  }
241
459
  const currentTierConfig = TIERS[currentTier];
242
460
 
243
- // Find which tier has this feature
244
- let requiredTier = null;
245
- let requiredTierConfig = null;
246
-
247
- for (const [tierName, tierConfig] of Object.entries(TIERS)) {
248
- if (tierConfig.features.includes(featureName)) {
249
- requiredTier = tierName;
250
- requiredTierConfig = tierConfig;
251
- break;
252
- }
253
- }
461
+ // Find which tier requires this feature using CLI_FEATURE_TIERS mapping
462
+ const requiredTier = CLI_FEATURE_TIERS[featureName];
254
463
 
255
- // If feature not found in any tier, deny access
464
+ // If feature not found in mapping, allow it (default to free)
256
465
  if (!requiredTier) {
257
466
  return {
258
- hasAccess: false,
467
+ hasAccess: true,
259
468
  tier: currentTier,
260
- reason: `${featureName} is not available in any tier`,
261
- upgradeUrl: 'https://vibecheckai.dev'
469
+ reason: `Feature '${featureName}' has no tier restriction`
262
470
  };
263
471
  }
264
472
 
473
+ const requiredTierConfig = TIERS[requiredTier];
474
+
265
475
  // Check if current tier meets minimum requirement (using order)
266
476
  const hasAccess = currentTierConfig.order >= requiredTierConfig.order;
267
477
 
@@ -287,7 +497,7 @@ export async function getFeatureAccessStatus(featureName, providedApiKey = null)
287
497
  */
288
498
  export function withTierCheck(featureName, handler) {
289
499
  return async (args) => {
290
- const access = await checkFeatureAccess(featureName, args?.apiKey);
500
+ const access = await getFeatureAccessStatus(featureName, args?.apiKey);
291
501
 
292
502
  if (!access.hasAccess) {
293
503
  return {
@@ -393,7 +603,7 @@ export async function getMcpToolAccess(toolName, providedApiKey = null) {
393
603
  */
394
604
  export function withMcpToolCheck(toolName, handler) {
395
605
  return async (args) => {
396
- const access = await checkMcpToolAccess(toolName, args?.apiKey);
606
+ const access = await getMcpToolAccess(toolName, args?.apiKey);
397
607
 
398
608
  if (!access.hasAccess) {
399
609
  return {
@@ -427,12 +637,17 @@ export async function getUserInfo() {
427
637
  const tier = getTierFromApiKey(config.apiKey);
428
638
  const tierConfig = TIERS[tier];
429
639
 
640
+ // Get features available for this tier from CLI_FEATURE_TIERS
641
+ const availableFeatures = Object.entries(CLI_FEATURE_TIERS)
642
+ .filter(([, reqTier]) => TIERS[reqTier].order <= tierConfig.order)
643
+ .map(([feature]) => feature);
644
+
430
645
  return {
431
646
  authenticated: true,
432
647
  tier,
433
648
  email: config.email,
434
649
  authenticatedAt: config.authenticatedAt,
435
- features: tierConfig.features,
650
+ features: availableFeatures,
436
651
  limits: tierConfig.limits,
437
652
  mcpTools: tierConfig.mcpTools,
438
653
  };