# Chron → Microsoft Sentinel

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.

## Prerequisites

- Microsoft Sentinel enabled on a Log Analytics workspace
- Azure subscription with permission to create App Registrations and Data Collection Rules

## Step 1 — Register an Azure App

1. Open **Azure Portal** → **Azure Active Directory** → **App registrations** → **New registration**
2. Name it `chron-ingest` (or any name), leave defaults, click **Register**
3. Note the **Application (client) ID** and **Directory (tenant) ID**
4. Go to **Certificates & secrets** → **New client secret** — copy the value immediately

## Step 2 — Create a custom table in Log Analytics

1. Open your Log Analytics workspace → **Tables** → **Create** → **New custom log (DCR-based)**
2. Name the table `ChronEvents_CL`
3. Define the schema (add these columns):

| Column | Type |
|---|---|
| `EventType` | string |
| `SessionIdPrefix` | string |
| `AiTool` | string |
| `OS` | string |
| `ChronVersion` | string |
| `Computer` | string |
| `Role` | string |
| `DetectionType` | string |
| `MaskedValue` | string |

(`TimeGenerated` is added automatically.)

4. The wizard creates a **Data Collection Endpoint (DCE)** and **Data Collection Rule (DCR)** — note both URLs/IDs.

## Step 3 — Grant the App ingest permission

1. Open the DCR you created → **Access control (IAM)** → **Add role assignment**
2. Role: **Monitoring Metrics Publisher**
3. Member: the `chron-ingest` app registration

## Step 4 — Get the DCR Immutable ID

Open the DCR → **Overview** → **JSON view** → copy the `immutableId` field (starts with `dcr-`).

## Step 5 — Connect from the developer machine

```bash
chron connect sentinel
```

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.

## Step 6 — Add env vars to your MCP client

The `connect` command prints the exact block to paste. For Claude Code, add to the `chron` entry in `~/.claude.json`:

```json
{
  "mcpServers": {
    "chron": {
      "type": "stdio",
      "command": "node",
      "args": ["/path/to/dist/index.js"],
      "env": {
        "CHRON_SENTINEL_TENANT_ID": "<your-tenant-id>",
        "CHRON_SENTINEL_CLIENT_ID": "<your-client-id>",
        "CHRON_SENTINEL_CLIENT_SECRET": "<your-client-secret>",
        "CHRON_SENTINEL_DCE": "https://<your-dce>.ingest.monitor.azure.com",
        "CHRON_SENTINEL_DCR_ID": "dcr-<your-immutable-id>",
        "CHRON_SENTINEL_STREAM": "Custom-ChronEvents_CL"
      }
    }
  }
}
```

## Step 7 — Add KQL queries to Sentinel

In Sentinel → **Logs**, run queries from this directory:

| File | Use |
|---|---|
| `events-by-type.kql` | Time-series — event volume by type |
| `sessions-by-developer.kql` | Table — sessions per machine and AI tool |
| `secrets-detected.kql` | Table — credential exposures for SOC review |
| `ai-tool-usage.kql` | Summary — tool adoption breakdown |
| `daily-active-developers.kql` | Time-series — distinct active machines per day |
| `secrets-alert.kql` | Scheduled alert rule — credential exposures |

## Step 8 — Set up the credential alert (recommended)

1. Sentinel → **Analytics** → **Create** → **Scheduled query rule**
2. Paste `secrets-alert.kql` as the rule query
3. Set **Run query every**: 1 hour, **Lookup data from the last**: 1 hour
4. Alert threshold: **Number of results greater than 0**
5. Route to your SOC incident queue

## Event schema

All events arrive in `ChronEvents_CL` with:

| Column | Present on |
|---|---|
| `TimeGenerated` | All events |
| `EventType` | All: `session_started`, `message_logged`, `secret_detected` |
| `SessionIdPrefix` | All events |
| `AiTool` | All events: `claude`, `cursor`, `windsurf`, etc. |
| `OS` | All events: `darwin`, `linux`, `win32` |
| `ChronVersion` | All events |
| `Computer` | All events: developer's machine hostname |
| `Role` | `message_logged` only: `user` or `assistant` |
| `DetectionType` | `secret_detected` only: `api_key`, `aws_key`, `jwt`, etc. |
| `MaskedValue` | `secret_detected` only: e.g. `AKIA****F3K2` |

**What is never transmitted:** message content, actual secret values, file paths, or any user-typed text.

## Token refresh

Chron automatically refreshes the Azure AD access token before it expires (tokens are valid for 1 hour). No manual intervention required after initial setup.
