chron-mcp 0.1.13 → 0.1.17

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
@@ -24,6 +24,8 @@ Chron fixes that. Every exchange is logged with a precise local datetime (includ
24
24
 
25
25
  ## Install
26
26
 
27
+ **MCP server** (for Claude Desktop, Claude Code, Cursor, Windsurf):
28
+
27
29
  Add to your AI tool's MCP config:
28
30
 
29
31
  ```json
@@ -37,10 +39,46 @@ Add to your AI tool's MCP config:
37
39
  }
38
40
  ```
39
41
 
42
+ **CLI** (for `chron history`, `chron connect`, `chron export`, etc.):
43
+
44
+ ```bash
45
+ npm install -g chron-mcp
46
+ ```
47
+
48
+ > `npx chron-mcp` starts the MCP server only — it does not put the `chron` CLI command in your PATH. You need a global install for the CLI.
49
+
40
50
  First run creates `~/.chron/chron.db` automatically. No database setup, no env vars, no migrations.
41
51
 
42
52
  ---
43
53
 
54
+ ## CLI
55
+
56
+ After `npm install -g chron-mcp`, the `chron` command is available globally:
57
+
58
+ ```
59
+ Usage: chron <command> [options]
60
+
61
+ Commands:
62
+ history List sessions or show full log for a session
63
+ report Aggregate audit stats across sessions
64
+ export Export a session as markdown
65
+ secrets List detected secrets across sessions
66
+ settings View current configuration
67
+ connect Connect to a SIEM integration (crowdstrike, sentinel, splunk)
68
+
69
+ Options (history):
70
+ --limit=<n> Max sessions to show (default: 20)
71
+ <id-prefix> Show full log for the session with this ID prefix
72
+
73
+ Options (report):
74
+ --since=<range> Filter by date: 7d, 30d, or YYYY-MM-DD (default: all time)
75
+
76
+ Options (export / secrets):
77
+ <id-prefix> Scope to a single session
78
+ ```
79
+
80
+ ---
81
+
44
82
  ## What it logs
45
83
 
46
84
  Every exchange is recorded with precise local timestamps — user message when received, assistant response when sent:
@@ -173,6 +211,261 @@ This turns your local log into a verifiable audit artifact. Any edit to a stored
173
211
 
174
212
  ---
175
213
 
214
+ ## CrowdStrike LogScale integration
215
+
216
+ Stream AI session telemetry directly from developer machines into your CrowdStrike LogScale repository. No relay service — events go straight to your own LogScale instance.
217
+
218
+ **What gets sent:** session starts, message counts (role only), and masked credential detections. Message content never leaves the machine.
219
+
220
+ ### Setup
221
+
222
+ **1. Create a LogScale repository and ingest token**
223
+
224
+ In Falcon console → **Log Management** → **Repositories** → **New repository** (name it `ChronAIEvents`).
225
+ Then **Settings** → **API Tokens** → **Add token** → select **Ingest** permission → copy the token.
226
+
227
+ Your ingest URL follows this pattern:
228
+ ```
229
+ https://<your-cluster>.humio.com/api/v1/ingest/humio-structured
230
+ ```
231
+
232
+ **2. Install the CLI and run the onboarding wizard**
233
+
234
+ ```bash
235
+ npm install -g chron-mcp
236
+ chron connect crowdstrike
237
+ # Paste your ingest URL and token when prompted
238
+ # The wizard sends a test event and confirms before saving
239
+ ```
240
+
241
+ **3. Add env vars to your MCP client config**
242
+
243
+ The wizard prints the exact block to paste. For Claude Code, add to the `chron` entry in `~/.claude.json`:
244
+
245
+ ```json
246
+ {
247
+ "mcpServers": {
248
+ "chron": {
249
+ "command": "npx",
250
+ "args": ["-y", "chron-mcp"],
251
+ "env": {
252
+ "CHRON_LOGSCALE_URL": "https://<your-cluster>.humio.com/api/v1/ingest/humio-structured",
253
+ "CHRON_LOGSCALE_TOKEN": "<your-ingest-token>"
254
+ }
255
+ }
256
+ }
257
+ }
258
+ ```
259
+
260
+ Restart your AI tool to pick up the new env vars.
261
+
262
+ **4. Import the pre-built dashboard**
263
+
264
+ In Falcon → **Log Management** → **Dashboards** → **Import**:
265
+ ```
266
+ node_modules/chron-mcp/dashboards/logscale/chron-ai-activity.yaml
267
+ ```
268
+
269
+ Full guide with SOC alert setup: [dashboards/logscale/README.md](dashboards/logscale/README.md)
270
+
271
+ ### Env vars
272
+
273
+ | Env var | Description |
274
+ |---|---|
275
+ | `CHRON_LOGSCALE_URL` | LogScale ingest endpoint |
276
+ | `CHRON_LOGSCALE_TOKEN` | LogScale ingest token |
277
+
278
+ Both must be set for events to flow. If unset, the integration is silently disabled.
279
+
280
+ ---
281
+
282
+ ## Splunk integration
283
+
284
+ Stream AI session telemetry into Splunk via HTTP Event Collector (HEC). Works with Splunk Enterprise, Splunk Cloud, and a local Docker instance.
285
+
286
+ **What gets sent:** session starts, message counts (role only), and masked credential detections. Message content never leaves the machine.
287
+
288
+ ### Setup
289
+
290
+ **Option A — Local Docker (fastest, no account needed)**
291
+
292
+ ```bash
293
+ # Start a local Splunk instance (Apple Silicon: --platform linux/amd64 is required)
294
+ docker run -d --name splunk-chron \
295
+ --platform linux/amd64 \
296
+ -p 8000:8000 -p 8088:8088 \
297
+ -e SPLUNK_START_ARGS=--accept-license \
298
+ -e SPLUNK_GENERAL_TERMS=--accept-sgt-current-at-splunk-com \
299
+ -e SPLUNK_PASSWORD=Admin1234! \
300
+ -e SPLUNK_HEC_TOKEN=chron-test-token \
301
+ splunk/splunk:latest
302
+
303
+ # Wait ~2 minutes, then watch until ready:
304
+ docker logs -f splunk-chron 2>&1 | grep -i "Ansible playbook complete"
305
+ ```
306
+
307
+ **Option B — Splunk Enterprise or Cloud**
308
+
309
+ Settings → **Data Inputs** → **HTTP Event Collector** → **New Token** → name it `chron-ingest` → source type `chron:event` → copy the token.
310
+
311
+ **2. Install the CLI and run the onboarding wizard**
312
+
313
+ ```bash
314
+ npm install -g chron-mcp
315
+ chron connect splunk
316
+ # For local Docker: URL = https://localhost:8088, Token = chron-test-token
317
+ # TLS verification is skipped automatically for localhost (self-signed cert)
318
+ ```
319
+
320
+ **3. Add env vars to your MCP client config**
321
+
322
+ The wizard prints the exact block to paste. For Claude Code, add to `~/.claude.json`:
323
+
324
+ ```json
325
+ {
326
+ "mcpServers": {
327
+ "chron": {
328
+ "command": "npx",
329
+ "args": ["-y", "chron-mcp"],
330
+ "env": {
331
+ "CHRON_SPLUNK_URL": "https://localhost:8088",
332
+ "CHRON_SPLUNK_TOKEN": "chron-test-token",
333
+ "CHRON_SPLUNK_INSECURE": "1"
334
+ }
335
+ }
336
+ }
337
+ }
338
+ ```
339
+
340
+ > Remove `CHRON_SPLUNK_INSECURE` for production Splunk instances with valid TLS certificates.
341
+
342
+ Restart your AI tool to pick up the new env vars.
343
+
344
+ **4. Search in Splunk**
345
+
346
+ Open [http://localhost:8000](http://localhost:8000) → **Search & Reporting** → run:
347
+ ```
348
+ sourcetype="chron:event"
349
+ ```
350
+
351
+ Events appear in real time as you have AI conversations.
352
+
353
+ Full guide with dashboard templates and alert setup: [dashboards/splunk/README.md](dashboards/splunk/README.md)
354
+
355
+ ### Env vars
356
+
357
+ | Env var | Description |
358
+ |---|---|
359
+ | `CHRON_SPLUNK_URL` | Splunk HEC base URL, e.g. `https://localhost:8088` or `https://your-host:8088` |
360
+ | `CHRON_SPLUNK_TOKEN` | HEC ingest token |
361
+ | `CHRON_SPLUNK_INSECURE` | Set to `1` to skip TLS verification (local Docker with self-signed cert) |
362
+
363
+ `CHRON_SPLUNK_URL` and `CHRON_SPLUNK_TOKEN` must both be set for events to flow.
364
+
365
+ ---
366
+
367
+ ## Microsoft Sentinel integration
368
+
369
+ Stream AI session telemetry into your Microsoft Sentinel workspace via the Azure Monitor Logs Ingestion API. Events go directly from developer machines to your own Log Analytics workspace — no relay service.
370
+
371
+ **What gets sent:** session starts, message counts (role only), and masked credential detections. Message content never leaves the machine.
372
+
373
+ ### Setup
374
+
375
+ **1. Register an Azure App**
376
+
377
+ Azure Portal → **Azure Active Directory** → **App registrations** → **New registration** → name it `chron-ingest`.
378
+ Note the **Application (client) ID** and **Directory (tenant) ID**.
379
+ Go to **Certificates & secrets** → **New client secret** → copy the value immediately.
380
+
381
+ **2. Create a custom table in Log Analytics**
382
+
383
+ Open your Log Analytics workspace → **Tables** → **Create** → **New custom log (DCR-based)** → name it `ChronEvents_CL`.
384
+
385
+ Add these columns (in addition to the auto-added `TimeGenerated`):
386
+
387
+ | Column | Type |
388
+ |---|---|
389
+ | `EventType` | string |
390
+ | `SessionIdPrefix` | string |
391
+ | `AiTool` | string |
392
+ | `OS` | string |
393
+ | `ChronVersion` | string |
394
+ | `Computer` | string |
395
+ | `Role` | string |
396
+ | `DetectionType` | string |
397
+ | `MaskedValue` | string |
398
+
399
+ The wizard creates a **Data Collection Endpoint (DCE)** and **Data Collection Rule (DCR)** — note both.
400
+
401
+ **3. Grant the App ingest permission**
402
+
403
+ Open the DCR → **Access control (IAM)** → **Add role assignment** → Role: **Monitoring Metrics Publisher** → Member: the `chron-ingest` app.
404
+
405
+ **4. Get the DCR Immutable ID**
406
+
407
+ Open the DCR → **Overview** → **JSON view** → copy the `immutableId` field (starts with `dcr-`).
408
+
409
+ **5. Install the CLI and run the onboarding wizard**
410
+
411
+ ```bash
412
+ npm install -g chron-mcp
413
+ chron connect sentinel
414
+ # Enter tenant ID, client ID, client secret, DCE URL, DCR immutable ID, and stream name
415
+ # The wizard authenticates with Azure AD and sends a test event before saving
416
+ ```
417
+
418
+ **6. Add env vars to your MCP client config**
419
+
420
+ The wizard prints the exact block to paste. For Claude Code, add to `~/.claude.json`:
421
+
422
+ ```json
423
+ {
424
+ "mcpServers": {
425
+ "chron": {
426
+ "command": "npx",
427
+ "args": ["-y", "chron-mcp"],
428
+ "env": {
429
+ "CHRON_SENTINEL_TENANT_ID": "<your-tenant-id>",
430
+ "CHRON_SENTINEL_CLIENT_ID": "<your-client-id>",
431
+ "CHRON_SENTINEL_CLIENT_SECRET": "<your-client-secret>",
432
+ "CHRON_SENTINEL_DCE": "https://<your-dce>.ingest.monitor.azure.com",
433
+ "CHRON_SENTINEL_DCR_ID": "dcr-<your-immutable-id>",
434
+ "CHRON_SENTINEL_STREAM": "Custom-ChronEvents_CL"
435
+ }
436
+ }
437
+ }
438
+ }
439
+ ```
440
+
441
+ Restart your AI tool to pick up the new env vars.
442
+
443
+ **7. Query in Sentinel**
444
+
445
+ Sentinel → **Logs** → paste any query from `dashboards/sentinel/`:
446
+ ```kql
447
+ ChronEvents_CL
448
+ | where TimeGenerated > ago(24h)
449
+ | summarize count() by EventType, AiTool
450
+ ```
451
+
452
+ Full guide with KQL queries and alert rule setup: [dashboards/sentinel/README.md](dashboards/sentinel/README.md)
453
+
454
+ ### Env vars
455
+
456
+ | Env var | Description |
457
+ |---|---|
458
+ | `CHRON_SENTINEL_TENANT_ID` | Azure AD tenant ID |
459
+ | `CHRON_SENTINEL_CLIENT_ID` | App Registration client ID |
460
+ | `CHRON_SENTINEL_CLIENT_SECRET` | App Registration client secret |
461
+ | `CHRON_SENTINEL_DCE` | Data Collection Endpoint URL |
462
+ | `CHRON_SENTINEL_DCR_ID` | DCR Immutable ID (starts with `dcr-`) |
463
+ | `CHRON_SENTINEL_STREAM` | Stream name, e.g. `Custom-ChronEvents_CL` |
464
+
465
+ All six must be set for events to flow. Token refresh is automatic (1-hour Azure AD tokens, refreshed 60s before expiry).
466
+
467
+ ---
468
+
176
469
  ## Configuration
177
470
 
178
471
  | Env var | Default | Description |
@@ -181,6 +474,18 @@ This turns your local log into a verifiable audit artifact. Any edit to a stored
181
474
  | `CHRON_TRANSPORT` | `stdio` | Set to `http` to enable HTTP+SSE mode |
182
475
  | `CHRON_API_KEY` | _(none)_ | Bearer token for HTTP mode |
183
476
  | `PORT` | `3001` | Port for HTTP mode |
477
+ | `CHRON_LOGSCALE_URL` | _(none)_ | LogScale ingest endpoint (enables CrowdStrike integration) |
478
+ | `CHRON_LOGSCALE_TOKEN` | _(none)_ | LogScale ingest token |
479
+ | `CHRON_SENTINEL_TENANT_ID` | _(none)_ | Azure AD tenant ID (enables Sentinel integration) |
480
+ | `CHRON_SENTINEL_CLIENT_ID` | _(none)_ | App Registration client ID |
481
+ | `CHRON_SENTINEL_CLIENT_SECRET` | _(none)_ | App Registration client secret |
482
+ | `CHRON_SENTINEL_DCE` | _(none)_ | Data Collection Endpoint URL |
483
+ | `CHRON_SENTINEL_DCR_ID` | _(none)_ | DCR Immutable ID |
484
+ | `CHRON_SENTINEL_STREAM` | _(none)_ | Stream name (e.g. `Custom-ChronEvents_CL`) |
485
+ | `CHRON_SPLUNK_URL` | _(none)_ | Splunk HEC base URL (enables Splunk integration) |
486
+ | `CHRON_SPLUNK_TOKEN` | _(none)_ | Splunk HEC ingest token |
487
+ | `CHRON_RELAY_URL` | _(none)_ | Generic relay endpoint (any SIEM or webhook) |
488
+ | `CHRON_RELAY_TOKEN` | _(none)_ | Bearer token for generic relay |
184
489
 
185
490
  ---
186
491
 
@@ -0,0 +1,121 @@
1
+ # Chron → CrowdStrike LogScale
2
+
3
+ Stream AI session telemetry directly from developer machines into your CrowdStrike LogScale repository. No relay service, no data routed through Chron infrastructure — events go from the developer's machine straight to your own LogScale instance.
4
+
5
+ ## Prerequisites
6
+
7
+ - CrowdStrike Falcon with **LogScale / Next-Gen SIEM** access
8
+ - Admin access to create a LogScale repository and ingest token
9
+
10
+ ## Step 1 — Create a LogScale repository
11
+
12
+ 1. Open Falcon console → **Log Management** → **Repositories**
13
+ 2. Click **New repository** → name it `ChronAIEvents` (or any name)
14
+ 3. Go to **Settings** → **API Tokens** → **Add token**
15
+ 4. Select **Ingest** permission, name it `chron-ingest`, copy the token
16
+
17
+ ## Step 2 — Get your cluster URL
18
+
19
+ Your LogScale ingest URL follows this pattern:
20
+
21
+ ```
22
+ https://<your-cluster>.humio.com/api/v1/ingest/humio-structured
23
+ ```
24
+
25
+ Common cluster URLs:
26
+ - US: `https://cloud.us.humio.com/api/v1/ingest/humio-structured`
27
+ - EU: `https://cloud.humio.com/api/v1/ingest/humio-structured`
28
+
29
+ Your exact URL is visible in Falcon under **Log Management → Repositories → <your repo> → Ingest API**.
30
+
31
+ ## Step 3 — Connect from the developer machine
32
+
33
+ Run the onboarding command:
34
+
35
+ ```bash
36
+ chron connect crowdstrike
37
+ ```
38
+
39
+ Paste the ingest URL and token when prompted. The command sends a test event and confirms the connection before saving.
40
+
41
+ ## Step 4 — Add env vars to your MCP client
42
+
43
+ The `connect` command prints the exact block to paste. For Claude Code, add to the `chron` entry in `~/.claude.json`:
44
+
45
+ ```json
46
+ {
47
+ "mcpServers": {
48
+ "chron": {
49
+ "type": "stdio",
50
+ "command": "node",
51
+ "args": ["/path/to/dist/index.js"],
52
+ "env": {
53
+ "CHRON_LOGSCALE_URL": "https://<your-cluster>.humio.com/api/v1/ingest/humio-structured",
54
+ "CHRON_LOGSCALE_TOKEN": "<your-ingest-token>"
55
+ }
56
+ }
57
+ }
58
+ }
59
+ ```
60
+
61
+ For org-wide rollout, push these env vars via MDM (Jamf, Intune, etc.) instead of configuring each machine individually.
62
+
63
+ ## Step 5 — Import the dashboard
64
+
65
+ In Falcon console → **Log Management** → **Dashboards** → **Import**:
66
+
67
+ ```
68
+ node_modules/chron-mcp/dashboards/logscale/chron-ai-activity.yaml
69
+ ```
70
+
71
+ The dashboard includes five widgets:
72
+ - **Daily Active Developers** — distinct machines with AI sessions per day
73
+ - **AI Tool Usage** — breakdown by tool (Claude, Cursor, Windsurf, etc.)
74
+ - **Secrets Detected by Type** — credential exposures over time
75
+ - **Sessions by Developer** — per-machine session count with AI tool
76
+ - **Credential Exposures** — full table of api_key, jwt, aws_key detections for SOC review
77
+
78
+ ## Step 6 — Set up the API key alert (optional)
79
+
80
+ In LogScale, create a **Scheduled Search** using `api-key-alert.lql`:
81
+
82
+ ```
83
+ event_type = "secret_detected"
84
+ | detection_type in ["api_key", "aws_key", "gcp_key", "azure_key", "private_key", "jwt"]
85
+ | table([#host, ai_tool, detection_type, masked_value, session_id_prefix, @timestamp])
86
+ ```
87
+
88
+ Set the alert to trigger when results > 0 and route to your SOC notification channel.
89
+
90
+ ## Event schema
91
+
92
+ All events arrive in LogScale with:
93
+
94
+ | Tag | Value |
95
+ |---|---|
96
+ | `#host` | Developer's machine hostname |
97
+ | `#source` | `chron-mcp` |
98
+
99
+ | Attribute | Present on |
100
+ |---|---|
101
+ | `event_type` | All events: `session_started`, `message_logged`, `secret_detected` |
102
+ | `session_id_prefix` | All events |
103
+ | `ai_tool` | All events: `claude`, `cursor`, `windsurf`, etc. |
104
+ | `os` | All events: `darwin`, `linux`, `win32` |
105
+ | `chron_version` | All events |
106
+ | `role` | `message_logged` only: `user` or `assistant` |
107
+ | `detection_type` | `secret_detected` only: `api_key`, `aws_key`, `jwt`, etc. |
108
+ | `masked_value` | `secret_detected` only: e.g. `AKIA****F3K2` |
109
+
110
+ **What is never transmitted:** message content, actual secret values, file paths, or any user-typed text.
111
+
112
+ ## Individual LQL queries
113
+
114
+ | File | Use |
115
+ |---|---|
116
+ | `secrets-by-type.lql` | Time-series widget — secret detections by type |
117
+ | `sessions-by-developer.lql` | Table — sessions per host and AI tool |
118
+ | `api-key-alert.lql` | Scheduled alert — credential exposures |
119
+ | `ai-tool-usage.lql` | Pie chart — tool adoption breakdown |
120
+ | `daily-active-developers.lql` | Time-series — distinct active machines per day |
121
+ | `chron-ai-activity.yaml` | Full importable dashboard (all five) |
@@ -0,0 +1,5 @@
1
+ // AI tool adoption breakdown — pie/bar widget
2
+ event_type = "session_started"
3
+ | groupby(ai_tool, function=count())
4
+ | rename(count(), as=sessions)
5
+ | sort(sessions, order=desc)
@@ -0,0 +1,5 @@
1
+ // Alert: API key or credential sent to AI tool
2
+ // Use as a Scheduled Search alert — notify SOC on any match
3
+ event_type = "secret_detected"
4
+ | detection_type in ["api_key", "aws_key", "gcp_key", "azure_key", "private_key", "jwt"]
5
+ | table([#host, ai_tool, detection_type, masked_value, session_id_prefix, @timestamp])
@@ -0,0 +1,66 @@
1
+ $schema: "https://schemas.humio.com/dashboard/v0.1.0"
2
+ name: "Chron — AI Activity"
3
+ updateFrequency: 60
4
+ timeSelector:
5
+ defaultRelativeTimeRange: "7d"
6
+
7
+ widgets:
8
+ - id: daily-active-developers
9
+ title: "Daily Active Developers"
10
+ type: time-chart
11
+ x: 0
12
+ y: 0
13
+ width: 8
14
+ height: 4
15
+ query: |
16
+ event_type = "session_started"
17
+ | timechart(unit=1d, function=count(#host, distinct=true))
18
+
19
+ - id: ai-tool-usage
20
+ title: "AI Tool Usage"
21
+ type: pie-chart
22
+ x: 8
23
+ y: 0
24
+ width: 4
25
+ height: 4
26
+ query: |
27
+ event_type = "session_started"
28
+ | groupby(ai_tool, function=count())
29
+ | rename(count(), as=sessions)
30
+ | sort(sessions, order=desc)
31
+
32
+ - id: secrets-by-type
33
+ title: "Secrets Detected by Type (7d)"
34
+ type: time-chart
35
+ x: 0
36
+ y: 4
37
+ width: 8
38
+ height: 4
39
+ query: |
40
+ event_type = "secret_detected"
41
+ | timechart(detection_type, unit=1d, function=count())
42
+
43
+ - id: sessions-by-developer
44
+ title: "Sessions by Developer"
45
+ type: table
46
+ x: 8
47
+ y: 4
48
+ width: 4
49
+ height: 4
50
+ query: |
51
+ event_type = "session_started"
52
+ | groupby([#host, ai_tool], function=count())
53
+ | rename(count(), as=sessions)
54
+ | sort(sessions, order=desc)
55
+
56
+ - id: api-key-alerts
57
+ title: "Credential Exposures — Requires Attention"
58
+ type: table
59
+ x: 0
60
+ y: 8
61
+ width: 12
62
+ height: 4
63
+ query: |
64
+ event_type = "secret_detected"
65
+ | detection_type in ["api_key", "aws_key", "gcp_key", "azure_key", "private_key", "jwt"]
66
+ | table([#host, ai_tool, detection_type, masked_value, session_id_prefix, @timestamp])
@@ -0,0 +1,3 @@
1
+ // Daily active developers — time-series widget
2
+ event_type = "session_started"
3
+ | timechart(unit=1d, function=count(#host, distinct=true))
@@ -0,0 +1,3 @@
1
+ // Secrets detected by type — timechart (use as time-series widget, 7d window)
2
+ event_type = "secret_detected"
3
+ | timechart(detection_type, unit=1d, function=count())
@@ -0,0 +1,6 @@
1
+ // Sessions per developer — bar/table widget
2
+ // #host is the actual hostname sent in the LogScale tags (set by chron MCP)
3
+ event_type = "session_started"
4
+ | groupby([#host, ai_tool], function=count())
5
+ | rename(count(), as=sessions)
6
+ | sort(sessions, order=desc)
@@ -0,0 +1,123 @@
1
+ # Chron → Microsoft Sentinel
2
+
3
+ Stream AI session telemetry directly from developer machines into your Microsoft Sentinel workspace via the Azure Monitor Logs Ingestion API. No relay service — events go from the developer's machine straight to your own Log Analytics workspace.
4
+
5
+ ## Prerequisites
6
+
7
+ - Microsoft Sentinel enabled on a Log Analytics workspace
8
+ - Azure subscription with permission to create App Registrations and Data Collection Rules
9
+
10
+ ## Step 1 — Register an Azure App
11
+
12
+ 1. Open **Azure Portal** → **Azure Active Directory** → **App registrations** → **New registration**
13
+ 2. Name it `chron-ingest` (or any name), leave defaults, click **Register**
14
+ 3. Note the **Application (client) ID** and **Directory (tenant) ID**
15
+ 4. Go to **Certificates & secrets** → **New client secret** — copy the value immediately
16
+
17
+ ## Step 2 — Create a custom table in Log Analytics
18
+
19
+ 1. Open your Log Analytics workspace → **Tables** → **Create** → **New custom log (DCR-based)**
20
+ 2. Name the table `ChronEvents_CL`
21
+ 3. Define the schema (add these columns):
22
+
23
+ | Column | Type |
24
+ |---|---|
25
+ | `EventType` | string |
26
+ | `SessionIdPrefix` | string |
27
+ | `AiTool` | string |
28
+ | `OS` | string |
29
+ | `ChronVersion` | string |
30
+ | `Computer` | string |
31
+ | `Role` | string |
32
+ | `DetectionType` | string |
33
+ | `MaskedValue` | string |
34
+
35
+ (`TimeGenerated` is added automatically.)
36
+
37
+ 4. The wizard creates a **Data Collection Endpoint (DCE)** and **Data Collection Rule (DCR)** — note both URLs/IDs.
38
+
39
+ ## Step 3 — Grant the App ingest permission
40
+
41
+ 1. Open the DCR you created → **Access control (IAM)** → **Add role assignment**
42
+ 2. Role: **Monitoring Metrics Publisher**
43
+ 3. Member: the `chron-ingest` app registration
44
+
45
+ ## Step 4 — Get the DCR Immutable ID
46
+
47
+ Open the DCR → **Overview** → **JSON view** → copy the `immutableId` field (starts with `dcr-`).
48
+
49
+ ## Step 5 — Connect from the developer machine
50
+
51
+ ```bash
52
+ chron connect sentinel
53
+ ```
54
+
55
+ Enter the tenant ID, client ID, client secret, DCE URL, DCR immutable ID, and stream name when prompted. The command authenticates, sends a test event, and confirms before saving.
56
+
57
+ ## Step 6 — Add env vars to your MCP client
58
+
59
+ The `connect` command prints the exact block to paste. For Claude Code, add to the `chron` entry in `~/.claude.json`:
60
+
61
+ ```json
62
+ {
63
+ "mcpServers": {
64
+ "chron": {
65
+ "type": "stdio",
66
+ "command": "node",
67
+ "args": ["/path/to/dist/index.js"],
68
+ "env": {
69
+ "CHRON_SENTINEL_TENANT_ID": "<your-tenant-id>",
70
+ "CHRON_SENTINEL_CLIENT_ID": "<your-client-id>",
71
+ "CHRON_SENTINEL_CLIENT_SECRET": "<your-client-secret>",
72
+ "CHRON_SENTINEL_DCE": "https://<your-dce>.ingest.monitor.azure.com",
73
+ "CHRON_SENTINEL_DCR_ID": "dcr-<your-immutable-id>",
74
+ "CHRON_SENTINEL_STREAM": "Custom-ChronEvents_CL"
75
+ }
76
+ }
77
+ }
78
+ }
79
+ ```
80
+
81
+ ## Step 7 — Add KQL queries to Sentinel
82
+
83
+ In Sentinel → **Logs**, run queries from this directory:
84
+
85
+ | File | Use |
86
+ |---|---|
87
+ | `events-by-type.kql` | Time-series — event volume by type |
88
+ | `sessions-by-developer.kql` | Table — sessions per machine and AI tool |
89
+ | `secrets-detected.kql` | Table — credential exposures for SOC review |
90
+ | `ai-tool-usage.kql` | Summary — tool adoption breakdown |
91
+ | `daily-active-developers.kql` | Time-series — distinct active machines per day |
92
+ | `secrets-alert.kql` | Scheduled alert rule — credential exposures |
93
+
94
+ ## Step 8 — Set up the credential alert (recommended)
95
+
96
+ 1. Sentinel → **Analytics** → **Create** → **Scheduled query rule**
97
+ 2. Paste `secrets-alert.kql` as the rule query
98
+ 3. Set **Run query every**: 1 hour, **Lookup data from the last**: 1 hour
99
+ 4. Alert threshold: **Number of results greater than 0**
100
+ 5. Route to your SOC incident queue
101
+
102
+ ## Event schema
103
+
104
+ All events arrive in `ChronEvents_CL` with:
105
+
106
+ | Column | Present on |
107
+ |---|---|
108
+ | `TimeGenerated` | All events |
109
+ | `EventType` | All: `session_started`, `message_logged`, `secret_detected` |
110
+ | `SessionIdPrefix` | All events |
111
+ | `AiTool` | All events: `claude`, `cursor`, `windsurf`, etc. |
112
+ | `OS` | All events: `darwin`, `linux`, `win32` |
113
+ | `ChronVersion` | All events |
114
+ | `Computer` | All events: developer's machine hostname |
115
+ | `Role` | `message_logged` only: `user` or `assistant` |
116
+ | `DetectionType` | `secret_detected` only: `api_key`, `aws_key`, `jwt`, etc. |
117
+ | `MaskedValue` | `secret_detected` only: e.g. `AKIA****F3K2` |
118
+
119
+ **What is never transmitted:** message content, actual secret values, file paths, or any user-typed text.
120
+
121
+ ## Token refresh
122
+
123
+ Chron automatically refreshes the Azure AD access token before it expires (tokens are valid for 1 hour). No manual intervention required after initial setup.
@@ -0,0 +1,7 @@
1
+ // AI tool adoption breakdown (last 30 days)
2
+ ChronEvents_CL
3
+ | where TimeGenerated > ago(30d)
4
+ | where EventType == "session_started"
5
+ | where isnotempty(AiTool)
6
+ | summarize Sessions = count() by AiTool
7
+ | sort by Sessions desc
@@ -0,0 +1,6 @@
1
+ // Distinct developer machines with AI sessions per day (last 30 days)
2
+ ChronEvents_CL
3
+ | where TimeGenerated > ago(30d)
4
+ | where EventType == "session_started"
5
+ | summarize ActiveDevelopers = dcount(Computer) by bin(TimeGenerated, 1d)
6
+ | render timechart