@probelabs/visor 0.1.130 → 0.1.131

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 (134) hide show
  1. package/README.md +7 -0
  2. package/defaults/visor.yaml +5 -2
  3. package/dist/ai-review-service.d.ts +2 -0
  4. package/dist/ai-review-service.d.ts.map +1 -1
  5. package/dist/cli-main.d.ts.map +1 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/config/cli-handler.d.ts +5 -0
  8. package/dist/config/cli-handler.d.ts.map +1 -0
  9. package/dist/config/config-reloader.d.ts +24 -0
  10. package/dist/config/config-reloader.d.ts.map +1 -0
  11. package/dist/config/config-snapshot-store.d.ts +21 -0
  12. package/dist/config/config-snapshot-store.d.ts.map +1 -0
  13. package/dist/config/config-watcher.d.ts +19 -0
  14. package/dist/config/config-watcher.d.ts.map +1 -0
  15. package/dist/config/types.d.ts +16 -0
  16. package/dist/config/types.d.ts.map +1 -0
  17. package/dist/defaults/visor.yaml +5 -2
  18. package/dist/docs/ai-configuration.md +139 -0
  19. package/dist/docs/ai-custom-tools.md +30 -0
  20. package/dist/docs/capacity-planning.md +359 -0
  21. package/dist/docs/commands.md +35 -0
  22. package/dist/docs/database-operations.md +487 -0
  23. package/dist/docs/index.md +6 -1
  24. package/dist/docs/licensing.md +372 -0
  25. package/dist/docs/production-deployment.md +583 -0
  26. package/dist/examples/ai-with-bash.yaml +17 -0
  27. package/dist/generated/config-schema.d.ts +4 -0
  28. package/dist/generated/config-schema.d.ts.map +1 -1
  29. package/dist/index.js +9945 -10907
  30. package/dist/liquid-extensions.d.ts +7 -0
  31. package/dist/liquid-extensions.d.ts.map +1 -1
  32. package/dist/output/traces/{run-2026-02-11T16-20-59-999Z.ndjson → run-2026-02-15T19-14-20-379Z.ndjson} +84 -84
  33. package/dist/{traces/run-2026-02-11T16-21-47-711Z.ndjson → output/traces/run-2026-02-15T19-15-09-410Z.ndjson} +1019 -1019
  34. package/dist/providers/ai-check-provider.d.ts +5 -0
  35. package/dist/providers/ai-check-provider.d.ts.map +1 -1
  36. package/dist/providers/command-check-provider.d.ts.map +1 -1
  37. package/dist/providers/workflow-check-provider.d.ts.map +1 -1
  38. package/dist/scheduler/schedule-tool.d.ts.map +1 -1
  39. package/dist/sdk/{check-provider-registry-PANIXYRB.mjs → check-provider-registry-AAPPJ4CP.mjs} +7 -7
  40. package/dist/sdk/{check-provider-registry-M3Y6JMTW.mjs → check-provider-registry-S7BMQ2FC.mjs} +7 -7
  41. package/dist/sdk/check-provider-registry-ZOLEYDKM.mjs +28 -0
  42. package/dist/sdk/{chunk-VMLORODQ.mjs → chunk-2GCSK3PD.mjs} +4 -4
  43. package/dist/sdk/{chunk-EUUAQBTW.mjs → chunk-6ZZ4DPAA.mjs} +240 -48
  44. package/dist/sdk/chunk-6ZZ4DPAA.mjs.map +1 -0
  45. package/dist/sdk/{chunk-HOKQOO3G.mjs → chunk-EBTD2D4L.mjs} +2 -2
  46. package/dist/sdk/chunk-LDFUW34H.mjs +39912 -0
  47. package/dist/sdk/chunk-LDFUW34H.mjs.map +1 -0
  48. package/dist/sdk/{chunk-UCNT3PDT.mjs → chunk-LQ5B4T6L.mjs} +5 -1
  49. package/dist/sdk/chunk-LQ5B4T6L.mjs.map +1 -0
  50. package/dist/sdk/{chunk-S6CD7GFM.mjs → chunk-MQ57AB4U.mjs} +211 -35
  51. package/dist/sdk/chunk-MQ57AB4U.mjs.map +1 -0
  52. package/dist/sdk/chunk-N4I6ZDCJ.mjs +436 -0
  53. package/dist/sdk/chunk-N4I6ZDCJ.mjs.map +1 -0
  54. package/dist/sdk/chunk-OMFPM576.mjs +739 -0
  55. package/dist/sdk/chunk-OMFPM576.mjs.map +1 -0
  56. package/dist/sdk/chunk-RI77TA6V.mjs +436 -0
  57. package/dist/sdk/chunk-RI77TA6V.mjs.map +1 -0
  58. package/dist/sdk/chunk-VO4N6TEL.mjs +1502 -0
  59. package/dist/sdk/chunk-VO4N6TEL.mjs.map +1 -0
  60. package/dist/sdk/{chunk-V2IV3ILA.mjs → chunk-XJQKTK6V.mjs} +31 -5
  61. package/dist/sdk/chunk-XJQKTK6V.mjs.map +1 -0
  62. package/dist/sdk/{config-OGOS4ZU4.mjs → config-4EG7IQIU.mjs} +2 -2
  63. package/dist/sdk/{failure-condition-evaluator-HC3M5377.mjs → failure-condition-evaluator-GLHZZF47.mjs} +3 -3
  64. package/dist/sdk/failure-condition-evaluator-KN55WXRO.mjs +17 -0
  65. package/dist/sdk/{github-frontend-E2KJSC3Y.mjs → github-frontend-F4TE2JY7.mjs} +3 -3
  66. package/dist/sdk/github-frontend-HCOKL53D.mjs +1356 -0
  67. package/dist/sdk/github-frontend-HCOKL53D.mjs.map +1 -0
  68. package/dist/sdk/{host-EE6EJ2FM.mjs → host-SAT6RHDX.mjs} +2 -2
  69. package/dist/sdk/host-VA3ET7N6.mjs +63 -0
  70. package/dist/sdk/host-VA3ET7N6.mjs.map +1 -0
  71. package/dist/sdk/{liquid-extensions-E4EUOCES.mjs → liquid-extensions-YDIIH33Q.mjs} +2 -2
  72. package/dist/sdk/{routing-OZQWAGAI.mjs → routing-KFYQGOYU.mjs} +5 -5
  73. package/dist/sdk/routing-OXQKETSA.mjs +25 -0
  74. package/dist/sdk/{schedule-tool-handler-IEB2VS7O.mjs → schedule-tool-handler-G353DHS6.mjs} +7 -7
  75. package/dist/sdk/{schedule-tool-handler-B7TMSG6A.mjs → schedule-tool-handler-OQF57URO.mjs} +7 -7
  76. package/dist/sdk/schedule-tool-handler-PJVKWSYX.mjs +38 -0
  77. package/dist/sdk/schedule-tool-handler-PJVKWSYX.mjs.map +1 -0
  78. package/dist/sdk/sdk.d.mts +15 -0
  79. package/dist/sdk/sdk.d.ts +15 -0
  80. package/dist/sdk/sdk.js +621 -183
  81. package/dist/sdk/sdk.js.map +1 -1
  82. package/dist/sdk/sdk.mjs +6 -6
  83. package/dist/sdk/{trace-helpers-PP3YHTAM.mjs → trace-helpers-LOPBHYYX.mjs} +4 -2
  84. package/dist/sdk/trace-helpers-LOPBHYYX.mjs.map +1 -0
  85. package/dist/sdk/trace-helpers-R2ETIEC2.mjs +25 -0
  86. package/dist/sdk/trace-helpers-R2ETIEC2.mjs.map +1 -0
  87. package/dist/sdk/{workflow-check-provider-2ET3SFZH.mjs → workflow-check-provider-57KAR4Y4.mjs} +7 -7
  88. package/dist/sdk/workflow-check-provider-57KAR4Y4.mjs.map +1 -0
  89. package/dist/sdk/{workflow-check-provider-HB4XTD4Z.mjs → workflow-check-provider-LRWD52WN.mjs} +7 -7
  90. package/dist/sdk/workflow-check-provider-LRWD52WN.mjs.map +1 -0
  91. package/dist/sdk/workflow-check-provider-N2DRFQDB.mjs +28 -0
  92. package/dist/sdk/workflow-check-provider-N2DRFQDB.mjs.map +1 -0
  93. package/dist/slack/socket-runner.d.ts.map +1 -1
  94. package/dist/state-machine/context/build-engine-context.d.ts.map +1 -1
  95. package/dist/state-machine/runner.d.ts.map +1 -1
  96. package/dist/state-machine/states/completed.d.ts.map +1 -1
  97. package/dist/telemetry/trace-helpers.d.ts +5 -0
  98. package/dist/telemetry/trace-helpers.d.ts.map +1 -1
  99. package/dist/test-runner/evaluators.d.ts.map +1 -1
  100. package/dist/test-runner/index.d.ts +7 -0
  101. package/dist/test-runner/index.d.ts.map +1 -1
  102. package/dist/test-runner/validator.d.ts.map +1 -1
  103. package/dist/traces/{run-2026-02-11T16-20-59-999Z.ndjson → run-2026-02-15T19-14-20-379Z.ndjson} +84 -84
  104. package/dist/{output/traces/run-2026-02-11T16-21-47-711Z.ndjson → traces/run-2026-02-15T19-15-09-410Z.ndjson} +1019 -1019
  105. package/dist/tui/chat-runner.d.ts.map +1 -1
  106. package/dist/types/cli.d.ts +2 -0
  107. package/dist/types/cli.d.ts.map +1 -1
  108. package/dist/types/config.d.ts +15 -0
  109. package/dist/types/config.d.ts.map +1 -1
  110. package/dist/types/engine.d.ts +2 -0
  111. package/dist/types/engine.d.ts.map +1 -1
  112. package/package.json +3 -3
  113. package/defaults/.visor.yaml +0 -420
  114. package/dist/sdk/chunk-EUUAQBTW.mjs.map +0 -1
  115. package/dist/sdk/chunk-S6CD7GFM.mjs.map +0 -1
  116. package/dist/sdk/chunk-UCNT3PDT.mjs.map +0 -1
  117. package/dist/sdk/chunk-V2IV3ILA.mjs.map +0 -1
  118. package/dist/sdk/chunk-YJRBN3XS.mjs +0 -217
  119. package/dist/sdk/chunk-YJRBN3XS.mjs.map +0 -1
  120. /package/dist/sdk/{check-provider-registry-M3Y6JMTW.mjs.map → check-provider-registry-AAPPJ4CP.mjs.map} +0 -0
  121. /package/dist/sdk/{check-provider-registry-PANIXYRB.mjs.map → check-provider-registry-S7BMQ2FC.mjs.map} +0 -0
  122. /package/dist/sdk/{config-OGOS4ZU4.mjs.map → check-provider-registry-ZOLEYDKM.mjs.map} +0 -0
  123. /package/dist/sdk/{chunk-VMLORODQ.mjs.map → chunk-2GCSK3PD.mjs.map} +0 -0
  124. /package/dist/sdk/{chunk-HOKQOO3G.mjs.map → chunk-EBTD2D4L.mjs.map} +0 -0
  125. /package/dist/sdk/{failure-condition-evaluator-HC3M5377.mjs.map → config-4EG7IQIU.mjs.map} +0 -0
  126. /package/dist/sdk/{liquid-extensions-E4EUOCES.mjs.map → failure-condition-evaluator-GLHZZF47.mjs.map} +0 -0
  127. /package/dist/sdk/{routing-OZQWAGAI.mjs.map → failure-condition-evaluator-KN55WXRO.mjs.map} +0 -0
  128. /package/dist/sdk/{github-frontend-E2KJSC3Y.mjs.map → github-frontend-F4TE2JY7.mjs.map} +0 -0
  129. /package/dist/sdk/{host-EE6EJ2FM.mjs.map → host-SAT6RHDX.mjs.map} +0 -0
  130. /package/dist/sdk/{schedule-tool-handler-B7TMSG6A.mjs.map → liquid-extensions-YDIIH33Q.mjs.map} +0 -0
  131. /package/dist/sdk/{schedule-tool-handler-IEB2VS7O.mjs.map → routing-KFYQGOYU.mjs.map} +0 -0
  132. /package/dist/sdk/{trace-helpers-PP3YHTAM.mjs.map → routing-OXQKETSA.mjs.map} +0 -0
  133. /package/dist/sdk/{workflow-check-provider-2ET3SFZH.mjs.map → schedule-tool-handler-G353DHS6.mjs.map} +0 -0
  134. /package/dist/sdk/{workflow-check-provider-HB4XTD4Z.mjs.map → schedule-tool-handler-OQF57URO.mjs.map} +0 -0
@@ -0,0 +1,583 @@
1
+ # Production Deployment Guide
2
+
3
+ This guide covers deploying Visor as a production service across Docker, Kubernetes, and traditional server environments.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ - [Architecture Overview](#architecture-overview)
10
+ - [Docker Deployment](#docker-deployment)
11
+ - [Docker Compose](#docker-compose)
12
+ - [Kubernetes Deployment](#kubernetes-deployment)
13
+ - [Environment Variables](#environment-variables)
14
+ - [Health Checks and Readiness](#health-checks-and-readiness)
15
+ - [Security Hardening](#security-hardening)
16
+ - [Multi-Instance / High Availability](#multi-instance--high-availability)
17
+ - [Logging and Monitoring](#logging-and-monitoring)
18
+ - [Backup and Recovery](#backup-and-recovery)
19
+ - [Upgrading](#upgrading)
20
+ - [Troubleshooting](#troubleshooting)
21
+
22
+ ---
23
+
24
+ ## Architecture Overview
25
+
26
+ A production Visor deployment typically consists of:
27
+
28
+ ```
29
+ +------------------+
30
+ | GitHub / Slack |
31
+ +--------+---------+
32
+ |
33
+ +----------+----------+
34
+ | Load Balancer / |
35
+ | Ingress Controller|
36
+ +----------+----------+
37
+ |
38
+ +----------------+----------------+
39
+ | | |
40
+ +-----v-----+ +-----v-----+ +-----v-----+
41
+ | Visor #1 | | Visor #2 | | Visor #3 |
42
+ | (--slack) | | (--slack) | | (--slack) |
43
+ +-----+------+ +-----+------+ +-----+------+
44
+ | | |
45
+ +----------------+----------------+
46
+ |
47
+ +---------v---------+
48
+ | PostgreSQL / |
49
+ | MySQL / MSSQL |
50
+ +-------------------+
51
+ ```
52
+
53
+ **Components:**
54
+ - **Visor instances** run in `--slack` mode (long-running) or as CI jobs (ephemeral)
55
+ - **Database** stores scheduler state and config snapshots (PostgreSQL recommended)
56
+ - **External services**: GitHub API, Slack API, AI providers (Gemini, Claude, OpenAI)
57
+
58
+ ---
59
+
60
+ ## Docker Deployment
61
+
62
+ ### Dockerfile
63
+
64
+ ```dockerfile
65
+ FROM node:20-alpine
66
+
67
+ WORKDIR /app
68
+
69
+ # Install Visor
70
+ RUN npm install -g @probelabs/visor
71
+
72
+ # For EE features with OPA policies:
73
+ # RUN npm install -g @probelabs/visor@ee
74
+ # RUN apk add --no-cache curl && \
75
+ # curl -L -o /usr/local/bin/opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64_static && \
76
+ # chmod +x /usr/local/bin/opa
77
+
78
+ # Copy configuration
79
+ COPY .visor.yaml /app/.visor.yaml
80
+
81
+ # Optional: copy policy files
82
+ # COPY policies/ /app/policies/
83
+
84
+ # Non-root user
85
+ RUN addgroup -g 1001 visor && \
86
+ adduser -D -u 1001 -G visor visor && \
87
+ chown -R visor:visor /app
88
+ USER visor
89
+
90
+ ENTRYPOINT ["visor"]
91
+ ```
92
+
93
+ ### Running
94
+
95
+ ```bash
96
+ # Build
97
+ docker build -t visor:latest .
98
+
99
+ # Run checks (ephemeral)
100
+ docker run --rm \
101
+ -e GITHUB_TOKEN="${GITHUB_TOKEN}" \
102
+ -e GEMINI_API_KEY="${GEMINI_API_KEY}" \
103
+ -v "$(pwd):/workspace" \
104
+ visor:latest --config /workspace/.visor.yaml --check all
105
+
106
+ # Run Slack mode (long-running)
107
+ docker run -d \
108
+ --name visor \
109
+ --restart unless-stopped \
110
+ -e SLACK_APP_TOKEN="${SLACK_APP_TOKEN}" \
111
+ -e GITHUB_TOKEN="${GITHUB_TOKEN}" \
112
+ -e GEMINI_API_KEY="${GEMINI_API_KEY}" \
113
+ visor:latest --slack --config /app/.visor.yaml --watch
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Docker Compose
119
+
120
+ ```yaml
121
+ version: "3.8"
122
+
123
+ services:
124
+ visor:
125
+ build: .
126
+ restart: unless-stopped
127
+ command: ["--slack", "--config", "/app/.visor.yaml", "--watch"]
128
+ environment:
129
+ SLACK_APP_TOKEN: "${SLACK_APP_TOKEN}"
130
+ GITHUB_TOKEN: "${GITHUB_TOKEN}"
131
+ GEMINI_API_KEY: "${GEMINI_API_KEY}"
132
+ # Database (EE)
133
+ VISOR_DB_PASSWORD: "${VISOR_DB_PASSWORD}"
134
+ # Telemetry
135
+ VISOR_TELEMETRY_ENABLED: "true"
136
+ VISOR_TELEMETRY_SINK: "otlp"
137
+ OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: "http://jaeger:4318/v1/traces"
138
+ volumes:
139
+ - visor-data:/app/.visor
140
+ - ./config/.visor.yaml:/app/.visor.yaml:ro
141
+ depends_on:
142
+ db:
143
+ condition: service_healthy
144
+
145
+ db:
146
+ image: postgres:16-alpine
147
+ restart: unless-stopped
148
+ environment:
149
+ POSTGRES_DB: visor
150
+ POSTGRES_USER: visor
151
+ POSTGRES_PASSWORD: "${VISOR_DB_PASSWORD}"
152
+ volumes:
153
+ - pgdata:/var/lib/postgresql/data
154
+ healthcheck:
155
+ test: ["CMD-SHELL", "pg_isready -U visor"]
156
+ interval: 10s
157
+ timeout: 5s
158
+ retries: 5
159
+
160
+ jaeger:
161
+ image: jaegertracing/all-in-one:latest
162
+ ports:
163
+ - "16686:16686" # UI
164
+ - "4318:4318" # OTLP HTTP
165
+
166
+ volumes:
167
+ visor-data:
168
+ pgdata:
169
+ ```
170
+
171
+ Start with:
172
+
173
+ ```bash
174
+ docker compose up -d
175
+ ```
176
+
177
+ ---
178
+
179
+ ## Kubernetes Deployment
180
+
181
+ ### Namespace and Secret
182
+
183
+ ```yaml
184
+ apiVersion: v1
185
+ kind: Namespace
186
+ metadata:
187
+ name: visor
188
+ ---
189
+ apiVersion: v1
190
+ kind: Secret
191
+ metadata:
192
+ name: visor-secrets
193
+ namespace: visor
194
+ type: Opaque
195
+ stringData:
196
+ SLACK_APP_TOKEN: "xapp-..."
197
+ GITHUB_TOKEN: "ghp_..."
198
+ GEMINI_API_KEY: "AIza..."
199
+ VISOR_DB_PASSWORD: "changeme"
200
+ # EE license (if applicable)
201
+ # VISOR_LICENSE: "eyJ..."
202
+ ```
203
+
204
+ ### ConfigMap
205
+
206
+ ```yaml
207
+ apiVersion: v1
208
+ kind: ConfigMap
209
+ metadata:
210
+ name: visor-config
211
+ namespace: visor
212
+ data:
213
+ .visor.yaml: |
214
+ version: "1.0"
215
+ checks:
216
+ security:
217
+ type: ai
218
+ prompt: "Review for security issues"
219
+ scheduler:
220
+ enabled: true
221
+ storage:
222
+ driver: postgresql
223
+ connection:
224
+ host: postgres.visor.svc.cluster.local
225
+ port: 5432
226
+ database: visor
227
+ user: visor
228
+ password: ${VISOR_DB_PASSWORD}
229
+ ssl: false
230
+ pool:
231
+ min: 2
232
+ max: 10
233
+ ha:
234
+ enabled: true
235
+ ```
236
+
237
+ ### Deployment
238
+
239
+ ```yaml
240
+ apiVersion: apps/v1
241
+ kind: Deployment
242
+ metadata:
243
+ name: visor
244
+ namespace: visor
245
+ spec:
246
+ replicas: 2
247
+ selector:
248
+ matchLabels:
249
+ app: visor
250
+ template:
251
+ metadata:
252
+ labels:
253
+ app: visor
254
+ spec:
255
+ containers:
256
+ - name: visor
257
+ image: your-registry/visor:latest
258
+ args: ["--slack", "--config", "/config/.visor.yaml", "--watch"]
259
+ envFrom:
260
+ - secretRef:
261
+ name: visor-secrets
262
+ volumeMounts:
263
+ - name: config
264
+ mountPath: /config
265
+ readOnly: true
266
+ - name: data
267
+ mountPath: /app/.visor
268
+ resources:
269
+ requests:
270
+ cpu: 250m
271
+ memory: 256Mi
272
+ limits:
273
+ cpu: "1"
274
+ memory: 512Mi
275
+ livenessProbe:
276
+ exec:
277
+ command: ["sh", "-c", "pgrep -f visor || exit 1"]
278
+ initialDelaySeconds: 10
279
+ periodSeconds: 30
280
+ readinessProbe:
281
+ exec:
282
+ command: ["sh", "-c", "pgrep -f visor || exit 1"]
283
+ initialDelaySeconds: 5
284
+ periodSeconds: 10
285
+ volumes:
286
+ - name: config
287
+ configMap:
288
+ name: visor-config
289
+ - name: data
290
+ emptyDir: {}
291
+ ```
292
+
293
+ ### PostgreSQL (StatefulSet)
294
+
295
+ For production PostgreSQL in Kubernetes, consider using an operator like [CloudNativePG](https://cloudnative-pg.io/) or a managed service (RDS, Cloud SQL, Azure Database). A minimal StatefulSet for reference:
296
+
297
+ ```yaml
298
+ apiVersion: apps/v1
299
+ kind: StatefulSet
300
+ metadata:
301
+ name: postgres
302
+ namespace: visor
303
+ spec:
304
+ serviceName: postgres
305
+ replicas: 1
306
+ selector:
307
+ matchLabels:
308
+ app: postgres
309
+ template:
310
+ metadata:
311
+ labels:
312
+ app: postgres
313
+ spec:
314
+ containers:
315
+ - name: postgres
316
+ image: postgres:16-alpine
317
+ env:
318
+ - name: POSTGRES_DB
319
+ value: visor
320
+ - name: POSTGRES_USER
321
+ value: visor
322
+ - name: POSTGRES_PASSWORD
323
+ valueFrom:
324
+ secretKeyRef:
325
+ name: visor-secrets
326
+ key: VISOR_DB_PASSWORD
327
+ ports:
328
+ - containerPort: 5432
329
+ volumeMounts:
330
+ - name: pgdata
331
+ mountPath: /var/lib/postgresql/data
332
+ volumeClaimTemplates:
333
+ - metadata:
334
+ name: pgdata
335
+ spec:
336
+ accessModes: ["ReadWriteOnce"]
337
+ resources:
338
+ requests:
339
+ storage: 5Gi
340
+ ---
341
+ apiVersion: v1
342
+ kind: Service
343
+ metadata:
344
+ name: postgres
345
+ namespace: visor
346
+ spec:
347
+ selector:
348
+ app: postgres
349
+ ports:
350
+ - port: 5432
351
+ ```
352
+
353
+ ### Triggering Config Reload in Kubernetes
354
+
355
+ When the ConfigMap changes, send `SIGUSR2` to the Visor process:
356
+
357
+ ```bash
358
+ kubectl exec -n visor deploy/visor -- kill -USR2 1
359
+ ```
360
+
361
+ Or use a sidecar like [reloader](https://github.com/stakater/Reloader) to auto-restart on ConfigMap changes.
362
+
363
+ ---
364
+
365
+ ## Environment Variables
366
+
367
+ | Variable | Required | Description |
368
+ |----------|----------|-------------|
369
+ | `GITHUB_TOKEN` | Yes (GitHub mode) | GitHub personal access token or app token |
370
+ | `SLACK_APP_TOKEN` | Yes (Slack mode) | Slack app-level token (`xapp-...`) |
371
+ | `GEMINI_API_KEY` | Provider-specific | Google Gemini API key |
372
+ | `ANTHROPIC_API_KEY` | Provider-specific | Anthropic Claude API key |
373
+ | `OPENAI_API_KEY` | Provider-specific | OpenAI API key |
374
+ | `VISOR_LICENSE` | EE only | Enterprise license JWT |
375
+ | `VISOR_LICENSE_FILE` | EE only | Path to license file (alternative to `VISOR_LICENSE`) |
376
+ | `VISOR_DB_PASSWORD` | EE + SQL | Database password (used in config via `${VISOR_DB_PASSWORD}`) |
377
+ | `VISOR_TELEMETRY_ENABLED` | No | Enable OpenTelemetry (`true`/`false`) |
378
+ | `VISOR_TELEMETRY_SINK` | No | Telemetry sink: `otlp`, `file`, `console` |
379
+ | `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | No | OTLP endpoint URL |
380
+ | `VISOR_WORKSPACE_PATH` | No | Override workspace base path |
381
+ | `VISOR_DEBUG` | No | Enable debug logging (`true`/`false`) |
382
+
383
+ ---
384
+
385
+ ## Security Hardening
386
+
387
+ ### Secrets Management
388
+
389
+ - **Never** commit secrets to `.visor.yaml`. Use environment variable references (`${VAR_NAME}`).
390
+ - Use Kubernetes Secrets, AWS Secrets Manager, or HashiCorp Vault for secret injection.
391
+ - Rotate API keys and tokens periodically.
392
+
393
+ ### Network
394
+
395
+ - Run Visor in a private subnet with outbound-only internet (for API calls to GitHub, Slack, AI providers).
396
+ - Restrict database access to Visor instances only (security groups / network policies).
397
+ - Use TLS for all database connections in production (`ssl: true`).
398
+
399
+ ### Container
400
+
401
+ - Run as non-root user (UID 1001 in the Dockerfile above).
402
+ - Use read-only root filesystem where possible.
403
+ - Set resource limits to prevent runaway processes.
404
+ - Pin image tags to specific versions (not `latest`).
405
+
406
+ ### EE Policy Engine
407
+
408
+ - Use `fallback: deny` to block unrecognized actions by default.
409
+ - Define explicit roles and scope policies to each team.
410
+ - Audit policy decisions with `fallback: warn` before switching to `deny`.
411
+
412
+ ---
413
+
414
+ ## Multi-Instance / High Availability
415
+
416
+ For multi-instance deployments (e.g., Kubernetes replicas > 1):
417
+
418
+ 1. **Use a SQL database** (PostgreSQL recommended) instead of SQLite.
419
+ 2. **Enable HA mode** in scheduler config:
420
+
421
+ ```yaml
422
+ scheduler:
423
+ storage:
424
+ driver: postgresql
425
+ connection:
426
+ host: postgres.visor.svc.cluster.local
427
+ database: visor
428
+ user: visor
429
+ password: ${VISOR_DB_PASSWORD}
430
+ ha:
431
+ enabled: true
432
+ lock_ttl: 60
433
+ heartbeat_interval: 15
434
+ ```
435
+
436
+ 3. **Each instance must have a unique `node_id`**. By default, Visor uses `hostname-pid` which is unique in containers.
437
+ 4. **Slack mode**: Multiple replicas can connect to the same Slack app. Socket Mode distributes events across connected instances.
438
+
439
+ ---
440
+
441
+ ## Logging and Monitoring
442
+
443
+ ### Structured Logging
444
+
445
+ Visor logs to stderr in structured format. Redirect to your log aggregator:
446
+
447
+ ```yaml
448
+ # Kubernetes: logs are collected automatically by fluentd/fluent-bit
449
+ # Docker: use --log-driver
450
+ docker run --log-driver=json-file --log-opt max-size=100m ...
451
+ ```
452
+
453
+ ### OpenTelemetry
454
+
455
+ Enable tracing for full execution visibility:
456
+
457
+ ```yaml
458
+ # .visor.yaml
459
+ telemetry:
460
+ enabled: true
461
+ sink: otlp
462
+ tracing:
463
+ auto_instrumentations: true
464
+ trace_report:
465
+ enabled: true
466
+ ```
467
+
468
+ ```bash
469
+ # Environment
470
+ VISOR_TELEMETRY_ENABLED=true
471
+ VISOR_TELEMETRY_SINK=otlp
472
+ OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://jaeger:4318/v1/traces
473
+ ```
474
+
475
+ See [Telemetry Setup](./telemetry-setup.md) and [Dashboards](./dashboards/README.md) for Grafana dashboard templates.
476
+
477
+ ### Key Metrics to Monitor
478
+
479
+ - **Check execution duration**: Track via OTel spans (`visor.check.*`)
480
+ - **Check success/failure rates**: Available in execution statistics
481
+ - **Scheduler lock contention**: Watch for `Timeout acquiring a connection` errors
482
+ - **AI provider latency and errors**: Track via provider-level spans
483
+ - **Memory and CPU usage**: Standard container metrics
484
+
485
+ ---
486
+
487
+ ## Backup and Recovery
488
+
489
+ ### SQLite (single-node)
490
+
491
+ ```bash
492
+ # Backup
493
+ cp .visor/schedules.db .visor/schedules.db.bak
494
+ cp .visor/config.db .visor/config.db.bak
495
+
496
+ # Restore
497
+ cp .visor/schedules.db.bak .visor/schedules.db
498
+ ```
499
+
500
+ ### PostgreSQL
501
+
502
+ ```bash
503
+ # Backup
504
+ pg_dump -h db.example.com -U visor visor > visor-backup.sql
505
+
506
+ # Restore
507
+ psql -h db.example.com -U visor visor < visor-backup.sql
508
+ ```
509
+
510
+ For automated backups, see [Database Operations](./database-operations.md).
511
+
512
+ ### Config Snapshots
513
+
514
+ Visor automatically snapshots resolved configuration at startup and on reload. Use the `visor config` command to list, view, and restore snapshots:
515
+
516
+ ```bash
517
+ visor config snapshots # List snapshots
518
+ visor config show 1 # View snapshot YAML
519
+ visor config restore 1 --output restored.yaml
520
+ ```
521
+
522
+ ---
523
+
524
+ ## Upgrading
525
+
526
+ ### Rolling Update (Kubernetes)
527
+
528
+ ```bash
529
+ # Update image
530
+ kubectl set image -n visor deployment/visor visor=your-registry/visor:v1.2.3
531
+
532
+ # Monitor rollout
533
+ kubectl rollout status -n visor deployment/visor
534
+ ```
535
+
536
+ ### Docker Compose
537
+
538
+ ```bash
539
+ docker compose pull
540
+ docker compose up -d
541
+ ```
542
+
543
+ ### Pre-upgrade Checklist
544
+
545
+ 1. **Read release notes** for breaking changes.
546
+ 2. **Back up the database** before upgrading.
547
+ 3. **Validate config** with the new version: `visor validate --config .visor.yaml`
548
+ 4. **Test in staging** before production rollout.
549
+ 5. For EE: Verify your license is compatible with the new version.
550
+
551
+ ---
552
+
553
+ ## Troubleshooting
554
+
555
+ ### Container Exits Immediately
556
+
557
+ - Check logs: `docker logs visor` or `kubectl logs -n visor deploy/visor`
558
+ - Verify `--config` path is correct and mounted.
559
+ - Ensure required environment variables are set.
560
+
561
+ ### Cannot Connect to Database
562
+
563
+ - Verify network connectivity: `nc -zv db.example.com 5432`
564
+ - Check credentials and database name.
565
+ - For Kubernetes: Ensure the database Service is in the same namespace or use FQDN.
566
+
567
+ ### Slack Mode Not Receiving Events
568
+
569
+ - Verify `SLACK_APP_TOKEN` is a valid app-level token (`xapp-...`).
570
+ - Check that Socket Mode is enabled in Slack app settings.
571
+ - Review Slack app event subscriptions.
572
+
573
+ ### Config Reload Not Working
574
+
575
+ - `--watch` requires `--config <path>` (explicit path, not auto-discovery).
576
+ - Check file permissions on the config file.
577
+ - Send `SIGUSR2` manually to test: `kill -USR2 $(pgrep -f visor)`
578
+
579
+ ### OOM Kills
580
+
581
+ - Increase memory limits in container spec.
582
+ - Reduce `--max-parallelism` to lower concurrent check count.
583
+ - Check for memory leaks in long-running Slack mode (report at [GitHub Issues](https://github.com/probelabs/visor/issues)).
@@ -120,6 +120,23 @@ steps:
120
120
  on: ["pr_opened"]
121
121
  tags: ["filesystem", "structure"]
122
122
 
123
+ # Example 7: Dynamic - Bash config from dependency output (ai_bash_config_js)
124
+ dynamic-bash-from-skills:
125
+ type: ai
126
+ depends_on: [build-config]
127
+ prompt: "Help the user with their request using available commands"
128
+ ai:
129
+ provider: anthropic
130
+ allowBash: true
131
+ bashConfig:
132
+ allow: ['gh:*'] # Static baseline commands
133
+ ai_bash_config_js: |
134
+ // Dynamically extend bash config from build-config output
135
+ // Active skills declare their allowed/disallowed commands
136
+ return outputs['build-config']?.bash_config ?? {};
137
+ on: ["manual"]
138
+ tags: ["dynamic", "skills"]
139
+
123
140
  output:
124
141
  pr_comment:
125
142
  enabled: true
@@ -457,6 +457,10 @@ export declare const configSchema: {
457
457
  readonly type: "string";
458
458
  readonly description: "JavaScript expression to dynamically compute custom tools for this AI check. Expression has access to: outputs, inputs, pr, files, env, memory Must return an array of tool names (strings) or WorkflowToolReference objects ({ workflow: string, args?: Record<string, unknown> })\n\nExample: ``` const tools = []; if (outputs['route-intent'].intent === 'engineer') { tools.push({ workflow: 'engineer', args: { projects: ['tyk'] } }); } return tools; ```";
459
459
  };
460
+ readonly ai_bash_config_js: {
461
+ readonly type: "string";
462
+ readonly description: "JavaScript expression to dynamically compute bash configuration for this AI check. Expression has access to: outputs, inputs, pr, files, env, memory. Must return a BashConfig object with optional allow/deny string arrays.\n\nExample: ``` return outputs['build-config']?.bash_config ?? {}; ```";
463
+ };
460
464
  readonly claude_code: {
461
465
  readonly $ref: "#/definitions/ClaudeCodeConfig";
462
466
  readonly description: "Claude Code configuration (for claude-code type checks)";
@@ -1 +1 @@
1
- {"version":3,"file":"config-schema.d.ts","sourceRoot":"","sources":["file:///home/runner/work/visor/visor/src/generated/config-schema.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+2Ef,CAAC;AACX,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"config-schema.d.ts","sourceRoot":"","sources":["file:///home/runner/work/visor/visor/src/generated/config-schema.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAo3Ef,CAAC;AACX,eAAe,YAAY,CAAC"}