Files
AutopilotMonitor/docs/architecture.md
T
2026-04-02 01:00:47 +02:00

1162 lines
55 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Autopilot Monitor – Architecture Guide
> Living document describing the system architecture, design decisions, and conventions.
---
## High-Level Overview
```text
+----------------------------------------------------------------------------------+
| Autopilot Monitor - Architecture |
+----------------------------------------------------------------------------------+
[Admin/User Browser]
|
| HTTPS
v
+-----------------------------+ OIDC +-------------------------------+
| Web App (Next.js) |<---------------->| Microsoft Identity (Entra ID) |
| Dashboard / Progress / Docs | | Auth / Authorization |
+-----------------------------+ +-------------------------------+
^ ^
| |
| | Live updates
| |
| | +---------------------------+
| +-------------------------| SignalR Hub |
| | Real-time event stream |
| +---------------------------+
| ^
| REST / API |
v | events
+---------------------------+ Data & |
| Azure Functions Backend |------> Integrations ---+
| APIs / Ingestion / Rules | \
+---------------------------+ \ +---------------------------+
^ +----->| Storage Tables |
| mTLS | Sessions / Events / Rules |
| telemetry / events +---------------------------+
| +---------------------------+
+---------------------------+ | Notifications |
| Agent on Windows Devices | | Teams / Slack / Telegram |
| Enrollment + app progress | +---------------------------+
+---------------------------+
+---------------------------+
| MCP Server |
| Azure Container App |
+---------------------------+
```
**Core Data Flow:**
```
Device (Agent) ──NDJSON+gzip──► Azure Functions ──► Azure Table Storage
│ │
├── SignalR ─────────►│
│ │
Web Dashboard ◄── REST API ◄──┘
```
| Component | Technology | Purpose |
|-----------|------------|---------|
| **Agent** | .NET Framework 4.8 (Windows) | Runs on devices during enrollment, collects telemetry |
| **SummaryDialog** | .NET Framework 4.8 (WPF) | Post-enrollment summary UI (outcome, app timeline, auto-close) |
| **Backend** | Azure Functions .NET 8 (Isolated Worker) | REST API, event processing, rule engine, storage |
| **Web** | Next.js 15 + TypeScript + React 18 | Dashboard, settings, analytics, real-time UI |
| **Shared** | .NET Standard 2.0 | DTOs, models, enums, constants shared between Agent & Backend |
| **MCP Server** | Azure Container App | Model Context Protocol server for AI-assisted analysis |
---
## Table of Contents
1. [Solution Structure](#solution-structure)
2. [Agent](#agent)
3. [Backend](#backend)
4. [Web Frontend](#web-frontend)
5. [Shared Library](#shared-library)
6. [Data Model](#data-model)
7. [Security Architecture](#security-architecture)
8. [Real-Time Communication](#real-time-communication)
9. [Rules Engine](#rules-engine)
10. [Session Lifecycle](#session-lifecycle)
11. [Configuration Hierarchy](#configuration-hierarchy)
12. [Diagnostics & Upload](#diagnostics--upload)
13. [Vulnerability Management](#vulnerability-management)
14. [Notification System](#notification-system)
15. [Testing](#testing)
16. [Infrastructure & Deployment](#infrastructure--deployment)
17. [Build & Development](#build--development)
---
## Solution Structure
```
AutopilotMonitor.sln
├── src/
│ ├── Shared/AutopilotMonitor.Shared/ (netstandard2.0)
│ ├── Agent/
│ │ ├── AutopilotMonitor.Agent/ (net48, Exe – entry point)
│ │ ├── AutopilotMonitor.Agent.Core/ (net48, Lib – core logic)
│ │ ├── AutopilotMonitor.Agent.Core.Tests/ (net48, xUnit – agent unit tests)
│ │ └── AutopilotMonitor.SummaryDialog/ (net48, WPF – enrollment summary UI)
│ ├── Backend/
│ │ ├── AutopilotMonitor.Functions/ (net8.0, Azure Functions v4)
│ │ └── AutopilotMonitor.Functions.Tests/ (net8.0, xUnit)
│ └── Web/
│ └── autopilot-monitor-web/ (Next.js 15, TypeScript)
├── rules/
│ ├── gather/ Individual gather rule JSONs
│ ├── analyze/ Individual analyze rule JSONs
│ ├── ime-log-patterns/ IME regex pattern JSONs
│ ├── schema/ JSON Schema definitions
│ ├── scripts/ combine.js
│ └── dist/ Combined output (embedded in Functions)
├── infra/ Bicep templates (MCP server)
├── scripts/
│ ├── Bootstrap/ Intune deployment scripts
│ └── Deployment/ CI/CD build scripts
└── .github/workflows/ CI/CD pipelines
```
**Project References:**
```
Shared ◄── Agent.Core ◄── Agent
◄── Agent.Core.Tests
◄── SummaryDialog
Shared ◄── Functions ◄── Functions.Tests
Web (independent – communicates via REST + SignalR)
```
---
## Agent
### Entry Point & Modes
**File:** `src/Agent/AutopilotMonitor.Agent/Program.cs`
Four execution modes:
1. **Normal mode** (default) – Main enrollment monitoring loop
2. **`--install` mode** – Deploys agent via Scheduled Task (Intune package)
3. **`--run-gather-rules` mode** – One-shot offline data collection, then exits
4. **`--run-ime-matching` mode** – Offline IME log parsing, writes `ime_pattern_matching.log`
Partial class files: `Program.Configuration.cs`, `Program.InstallMode.cs`, `Program.GatherRulesMode.cs`, `Program.ImeMatchingMode.cs`
### Startup Sequence (Normal Mode)
1. Single-instance check (prevent duplicate agent processes)
2. Self-update: `SelfUpdater.CheckAndApplyUpdateAsync()` (downloads from Azure Blob)
3. Load configuration
4. Check enrollment-complete marker (handles post-reboot cleanup retry)
5. Check session age emergency break (zombie agent kill-switch)
6. Detect previous exit type: `clean` / `exception_crash` / `hard_kill` / `reboot_kill` / `first_run`
7. `FetchRemoteConfig()` – Backend config with 15s timeout, disk cache fallback
8. `RegisterSessionAsync()` – Register session (5 retries, exponential backoff)
9. `StartWatching()` – Enable FileSystemWatcher for event spool
10. `StartEventCollectors()` – EspAndHelloTracker (always on)
11. `StartOptionalCollectors()` – PerformanceCollector, AgentSelfMetrics, EnrollmentTracker, DesktopArrivalDetector
12. `StartGatherRuleExecutor()` – Backend-defined data collection rules
13. `InitializeAnalyzers()` + `RunStartupAnalyzers()` – Security baseline
### Key Services
| Service | Location | Responsibility |
|---------|----------|----------------|
| `MonitoringService` | `Core/Monitoring/Core/` | Main orchestrator: starts/stops collectors, manages upload loop (3 partial files) |
| `BackendApiClient` | `Core/Monitoring/Network/` | HTTP client with mTLS cert + hardware headers + bootstrap token |
| `EventSpool` | `Core/Monitoring/Core/` | Offline event storage (JSON files), FileSystemWatcher-based |
| `EnrollmentTracker` | `Core/Monitoring/Tracking/` | Central enrollment state machine, 3 completion paths (4 partial files) |
| `ImeLogTracker` | `Core/Monitoring/Tracking/` | Parses IME logs with backend-provided regex patterns (3 partial files) |
| `EspAndHelloTracker` | `Core/Monitoring/Collectors/` | ESP state + Hello provisioning monitoring (4 partial files) |
| `DesktopArrivalDetector` | `Core/Monitoring/Collectors/` | Polls for explorer.exe under real user (non-SYSTEM, non-DefaultUser*) |
| `PerformanceCollector` | `Core/Monitoring/Collectors/` | CPU/Memory/Disk/Network metrics |
| `AgentSelfMetricsCollector` | `Core/Monitoring/Collectors/` | Agent process self-telemetry |
| `GatherRuleExecutor` | `Core/Monitoring/Collectors/` | Executes backend-defined data collection rules |
| `DeviceInfoCollector` | `Core/Monitoring/Collectors/` | Hardware spec + network/security info (3 partial files) |
| `DiagnosticsPackageService` | `Core/Monitoring/Core/` | Creates ZIP + uploads via short-lived SAS URL |
| `RemoteConfigService` | `Core/Configuration/` | Fetches & caches backend config with disk fallback |
| `SessionPersistence` | `Core/Monitoring/Core/` | Persists session ID, sequence counter, WhiteGlove state |
| `DistressReporter` | `Core/Monitoring/Network/` | Pre-auth distress signals (no cert required) |
| `EmergencyReporter` | `Core/Monitoring/Network/` | Posts AgentErrorReport via BackendApiClient |
| `GeoLocationService` | `Core/Monitoring/Network/` | IP-based device location lookup |
| `NtpTimeCheckService` | `Core/Monitoring/Network/` | UDP NTP query to detect clock skew |
| `TimezoneService` | `Core/Monitoring/Network/` | Sets Windows timezone via `tzutil /s` |
### Analyzers
| Analyzer | Purpose |
|----------|---------|
| `LocalAdminAnalyzer` | Enumerates local admin accounts at start+shutdown, flags unexpected accounts |
| `SoftwareInventoryAnalyzer` | Captures installed software from registry at start+shutdown, emits `software_inventory_analysis` events |
### Gather Rule Collectors
Each implements `IGatherRuleCollector`:
| Collector | Purpose |
|-----------|---------|
| `RegistryCollector` | Read registry values |
| `WmiCollector` | WMI queries |
| `EventLogCollector` | Windows Event Log queries |
| `FileCollector` | Collect file contents |
| `CommandCollector` | Run allowlisted commands |
| `LogParserCollector` | Parse log files |
| `JsonCollector` | Collect JSON from file/registry |
| `XmlCollector` | Parse XML files |
Security enforced by `GatherRuleGuards` (allowed targets) and `DiagnosticsPathGuards` (path validation, respects `UnrestrictedMode`).
### Directory Layout (Agent.Core)
```
Configuration/ Config loading, remote config service
Logging/ AgentLogger (file + optional console)
Monitoring/
├── Analyzers/ Security checks (LocalAdminAnalyzer, SoftwareInventoryAnalyzer)
├── Collectors/ Data collectors (ESP, Hello, Performance, GatherRules, Diagnostics, Desktop)
│ └── GatherCollectors/ Per-type gather rule collectors
├── Core/ Orchestration (MonitoringService, EventSpool, SessionPersistence, CleanupService)
├── Interop/ P/Invoke declarations (process creation, registry change notifications)
├── Network/ API client, distress/emergency reporters, geo-location, NTP, network metrics
├── Replay/ Log replay for testing/simulation
└── Tracking/ Enrollment state machine, IME parser, script/app tracking, state persistence
Security/ Certificate helper, enrollment awaiter, hardware info
```
### Event Collection & Upload
1. Collector emits `EnrollmentEvent` → sequence number auto-assigned (thread-safe Interlocked)
2. Event saved to spool as JSON file: `event_{timestamp}_{sequence}.json`
3. FileSystemWatcher triggers debounce timer (configurable `UploadIntervalSeconds`, default 30s)
4. Batch upload: NDJSON + gzip compression, max 100 events per batch
5. Response handling: `DeviceKillSignal` → self-destruct; `DeviceBlocked` → stop uploads
### Idle Timeout & Lifetime
- **Collector Idle Timeout**: Default 15min (`CollectorIdleTimeoutMinutes`). Tracks `_lastRealEventTime`. "Real" events = everything except `performance_snapshot`, `agent_metrics_snapshot`, and `*_stopped` variants. Idle collectors auto-restart on new activity.
- **Agent Max Lifetime**: Default 360min/6h (`AgentMaxLifetimeMinutes`). Emits `enrollment_failed` with `failureType="agent_timeout"`.
- **Session Age Emergency Break**: 48h absolute max. Checked at startup, triggers cleanup.
### Agent Data Paths
```
%ProgramData%\AutopilotMonitor\
├── session.id, session.seq, session.created
├── whiteglove.complete
├── bootstrap-config.json
├── Logs/agent.log
├── Spool/event_*.json
├── Config/remote-config.json
└── State/
├── enrollment-state.json
├── ime-tracker-state.json
├── enrollment-complete.marker
└── self-update-info.json
```
---
## Backend
### Azure Functions Setup
- **Runtime:** .NET 8 Isolated Worker, Azure Functions v4
- **Route Prefix:** `/api`
- **Monitoring:** Application Insights with sampling
- **Entry Point:** `src/Backend/AutopilotMonitor.Functions/Program.cs`
### Middleware Pipeline
Registered in this order:
1. `RequestTelemetryMiddleware` – Wraps all requests for Application Insights telemetry
2. `CorrelationIdMiddleware` – Propagates or generates `X-Correlation-Id`
3. `AuthenticationMiddleware` – Validates JWT Bearer tokens via Azure AD OIDC metadata; caches per-tenant config managers (max 100); tracks MCP usage via `X-Client-Source: mcp` header
4. `PolicyEnforcementMiddleware` – Catalog-driven authorization (fail-closed: unregistered routes get 403); sets `RequestContext` (IsGlobalAdmin, IsTenantAdmin, UserRole)
5. `UserRateLimitMiddleware` – Per-user API rate limiting for human users
### Endpoints (~140 Functions)
**Agent-to-Cloud (device auth via cert/bootstrap):**
| Route | Method | Purpose |
|-------|--------|---------|
| `/agent/register-session` | POST | Register new enrollment session |
| `/agent/ingest` | POST | Upload events (NDJSON+gzip) |
| `/agent/config` | GET | Fetch agent configuration |
| `/agent/upload-url` | POST | Get short-lived SAS URL for diagnostics |
| `/agent/error` | POST | Report agent errors |
| `/agent/distress` | POST | Pre-auth distress signal (no cert required) |
**Bootstrap (pre-MDM auth):**
| Route | Method | Purpose |
|-------|--------|---------|
| `/bootstrap/sessions` | POST/GET | Create/list bootstrap sessions |
| `/bootstrap/sessions/{code}` | DELETE | Revoke bootstrap session |
| `/bootstrap/validate/{code}` | GET | Validate bootstrap code (public) |
| `/bootstrap/register-session` | POST | Register via bootstrap token |
| `/bootstrap/ingest` | POST | Ingest via bootstrap token |
| `/bootstrap/config` | POST | Config via bootstrap token |
| `/bootstrap/error` | POST | Error report via bootstrap token |
**Web Portal (JWT auth):**
| Category | Key Routes |
|----------|------------|
| **Sessions** | `GET /sessions`, `GET /sessions/{id}`, `GET /sessions/{id}/events`, `DELETE /sessions/{id}`, `POST /sessions/{id}/mark-failed`, `POST /sessions/{id}/mark-succeeded` |
| **Search** | `GET /search/quick`, `GET /search/sessions`, `GET /search/sessions-by-event`, `GET /search/sessions-by-cve` |
| **Rules** | CRUD for `/rules/gather`, `/rules/analyze`, `/rules/ime-log-patterns`, `POST /rules/reseed-from-github`, `GET /rules/results` |
| **Config** | `GET/PUT /global/config`, `GET/PUT /config/{tenantId}`, `GET /config/all`, `GET /global/config/plan-tiers` |
| **Auth** | `GET /auth/me` |
| **Tenants** | CRUD `/tenants/{id}/admins`, `POST /tenants/{id}/offboard` |
| **Devices** | `POST /devices/block`, `DELETE /devices/block/{serial}`, `GET /devices/blocked`, `GET /global/devices/blocked` |
| **Versions** | `POST /versions/block`, `GET /versions/blocked`, `DELETE /versions/block/{pattern}` |
| **Reports** | `POST /sessions/{id}/report`, `GET/POST /global/session-reports`, distress reports |
| **Metrics** | `GET /metrics/usage`, `GET /metrics/app`, `GET /metrics/summary`, `GET /metrics/geographic`, `GET /metrics/geographic/sessions`, `GET /stats/platform` |
| **Global** | `/global/metrics/*`, `/global/audit/logs`, `/global/session-reports`, `/global/notifications` (CRUD + dismiss), `/global/mcp-users` |
| **Vulnerability** | `GET /sessions/{id}/vulnerability-report`, CPE mapping CRUD, `POST /vulnerability/sync`, software inventory |
| **Progress** | `GET /progress/sessions` |
| **Diagnostics** | `GET /diagnostics/download-url` |
| **SignalR** | `POST /realtime/negotiate`, `POST /realtime/groups/join`, `POST /realtime/groups/leave` |
| **Health** | `GET /health`, `GET /health/detailed` |
| **Feedback** | `POST /feedback` |
| **MCP** | `GET/POST/DELETE /global/mcp-users`, MCP usage metrics |
| **Raw/Debug** | AppInsights query proxy, raw events/sessions query, table query |
| **Autopilot Validation** | Consent URL + status for Graph API access |
**Timer Triggers:**
- `MaintenanceFunction` – Every 2 hours (`0 0 */2 * * *`): stale session detection, metrics aggregation, data cleanup
- `VulnerabilityDataSyncFunction` – Periodic CVE data sync from NVD/MSRC/CISA KEV
### Key Backend Services
| Service | Responsibility |
|---------|----------------|
| `TableStorageService` | Core data access for all 33 Azure Tables (split across 5 partial files) |
| `TenantConfigurationService` | Per-tenant config with 5-min cache |
| `AdminConfigurationService` | Global config with 5-min cache, syncs rate limits to tenants |
| `RateLimitService` | In-memory sliding window rate limiting (1-min window) |
| `DistressRateLimitService` | Rate limiter for the unauthenticated distress endpoint |
| `SecurityValidator` | Centralized request validation (cert → rate limit → hardware → APV) |
| `RuleEngine` | Server-side analyze rule evaluation with confidence scoring |
| `MaintenanceService` | Cleanup, metrics aggregation, stale session detection (+ Aggregation partial) |
| `BootstrapSessionService` | Bootstrap token lifecycle (create, validate, revoke) |
| `BlockedDeviceService` | Device block/kill signal management |
| `BlockedVersionService` | Version-based block/kill rules with wildcard and ceiling patterns |
| `SessionReportService` | Report ZIP generation + Blob upload |
| `BlobStorageService` | Azure Blob Storage (diagnostics upload SAS URLs) |
| `GraphTokenService` | MS Graph token acquisition for Autopilot device validation |
| `AutopilotDeviceValidator` | Validates serial against Intune Autopilot device list |
| `CorporateIdentifierValidator` | Validates against Intune Corporate Device Identifiers |
| `GlobalNotificationService` | Persistent in-app notifications for Global Admins |
| `HealthCheckService` | Health checks for Storage, Processing, and Agent binary availability |
| `PreviewWhitelistService` | Private Preview tenant whitelist with 5-min cache |
| `McpUserService` | MCP API user management + access control |
| `TenantAdminsService` | Tenant member/admin management (roles: Admin, Operator, Viewer) |
| `GlobalAdminService` | Global admin lookup |
| `UsageMetricsService` | Daily/rolling usage metrics |
| `PlatformMetricsService` | Platform-wide stats |
| `VulnerabilityCorrelationService` | Matches installed software against CVE data |
| `WebhookNotificationService` | Dispatches enrollment notifications via webhook (Teams/Slack) |
| `TelegramNotificationService` | Telegram bot notifications |
| `ResendEmailService` | Transactional emails via Resend API |
| `SignalRNotificationService` | Pushes real-time updates to connected web clients |
| `EventTimestampValidator` | Clamps/validates event timestamps (preserves originals) |
| `GitHubRuleRepository` | Fetches rules from GitHub for reseed |
### Data Access Layer
Repository pattern via interfaces in `AutopilotMonitor.Shared/DataAccess/`:
| Repository | Purpose |
|------------|---------|
| `TableSessionRepository` | Sessions + events + indexes |
| `TableConfigRepository` | Tenant/admin configuration |
| `TableRuleRepository` | Gather/analyze rules, IME patterns, rule results |
| `TableMetricsRepository` | Usage metrics, app install summaries, platform stats |
| `TableAdminRepository` | Audit logs, global/tenant admins, MCP users |
| `TableBootstrapRepository` | Bootstrap sessions |
| `TableDeviceSecurityRepository` | Blocked devices, blocked versions |
| `TableDistressReportRepository` | Distress reports |
| `TableMaintenanceRepository` | Maintenance operations |
| `TableNotificationRepository` | Global notifications |
| `TableUserUsageRepository` | Per-user API usage tracking |
| `TableVulnerabilityRepository` | CVE data, CPE mappings, software inventory |
### Event Processing Pipeline
```
Agent POST /api/agent/ingest (NDJSON+gzip)
│
├─ SecurityValidator.ValidateRequestAsync()
│ ├─ Tenant existence & suspension
│ ├─ Bootstrap token gate (if present)
│ ├─ Certificate validation against Intune CAs
│ ├─ Rate limiting (sliding window)
│ ├─ Hardware whitelist
│ └─ Autopilot device validation (optional)
│
├─ BlockedDeviceService.IsBlockedAsync() → kill/block signal
│
├─ ParseNdjsonGzipRequest() → decompress + parse events
│
├─ StampServerFields() → ReceivedAt, TenantId, SessionId
│
├─ EventTimestampValidator → clamp/validate timestamps
│
├─ TableStorageService.StoreEventsBatchAsync()
│
├─ ClassifyEvents()
│ ├─ Extract geo-location
│ ├─ Track app installs → AppInstallSummaries table
│ └─ Detect enrollment completion/failure
│
├─ UpdateSessionStatusAsync() → merge session row
│
├─ RuleEngine.AnalyzeSessionAsync() (on enrollment end)
│ └─ StoreRuleResultAsync() → RuleResults table
│
├─ VulnerabilityCorrelation (on software inventory events)
│
├─ WebhookNotificationService (Teams/Slack on enrollment complete/fail)
│
└─ SignalR broadcasts:
├─ "eventReceived" → tenant-{tenantId}
├─ "sessionStatusChanged" → tenant-{tenantId}
└─ "ruleResultReceived" → tenant-{tenantId}
```
---
## Web Frontend
### Technology Stack
- **Framework:** Next.js 15.1.6 (App Router)
- **Language:** TypeScript 5.7.3
- **UI:** React 18.2.0 + Tailwind CSS 3.4.17 (dark mode via `class` strategy)
- **Auth:** MSAL.js 3.28 (`@azure/msal-browser` + `@azure/msal-react`)
- **Real-time:** `@microsoft/signalr` 10.0.0
- **Maps:** Leaflet 1.9.4 + react-leaflet
- **Compression:** fflate 0.8.2
- **Telemetry:** Application Insights Web 3.3.11
### Page Routes
| Path | Purpose | Auth | Role |
|------|---------|------|------|
| `/` | Landing page with platform stats | Public | — |
| `/dashboard` | Session list, real-time updates | Yes | Any |
| `/sessions/[sessionId]` | Session detail, event timeline, analysis, vulnerability report | Yes | Any |
| `/diagnosis/[sessionId]` | Simplified diagnosis with badges | Yes | Any |
| `/fleet-health` | App metrics, fleet analytics | Yes | Member |
| `/geographic-performance` | Geo map + session drill-down | Yes | Member |
| `/analyze-rules` | Analyze rule CRUD | Yes | Tenant Admin |
| `/gather-rules` | Gather rule CRUD | Yes | Tenant Admin |
| `/ime-log-patterns` | IME pattern management | Yes | Tenant Admin |
| `/audit` | Audit log viewer | Yes | Member |
| `/usage-metrics` | Tenant usage analytics | Yes | Member |
| **Settings** | | | |
| `/settings/tenant/[section]` | Access management, Autopilot validation, bootstrap, hardware whitelist, notifications | Yes | Tenant Admin |
| `/settings/agent/[section]` | Agent analyzers, agent settings, diagnostics, unrestricted mode | Yes | Tenant Admin |
| `/settings/management/[section]` | Data management, offboarding | Yes | Tenant Admin |
| `/settings/reporting/[section]` | MCP usage | Yes | Tenant Admin |
| **Admin (Global)** | | | |
| `/admin/metrics/[section]` | Agent metrics, MCP usage, platform usage | Yes | Global Admin |
| `/admin/reports/[section]` | Distress reports, session export, session reports, user feedback | Yes | Global Admin |
| `/admin/security/[section]` | Device block, version block, vulnerability data | Yes | Global Admin |
| `/admin/settings/[section]` | Config reseed, diagnostics log paths, global settings, MCP users, usage plans | Yes | Global Admin |
| `/admin/tenants/[section]` | Tenant config report, tenant management | Yes | Global Admin |
| `/admin/ops` | Operations page | Yes | Global Admin |
| `/admin/software` | Software inventory | Yes | Global Admin |
| **Public** | | | |
| `/progress` | Real-time enrollment progress (end users) | Public | — |
| `/docs/[section]` | Documentation (12 sections) | Public | — |
| `/changelog` | Platform change log & known issues | Public | — |
| `/roadmap` | Planned features & current focus areas | Public | — |
| `/about` | Platform introduction & quick links | Public | — |
| `/privacy` | Privacy policy & data handling | Public | — |
| `/terms` | Terms of use & legal disclaimers | Public | — |
| `/preview` | Private Preview waitlist/approval | Yes | Unapproved |
| `/health-check` | Backend health status | Public | — |
| `/go/[code]` | Bootstrap short-link redirector | Public | — |
### State Management
React Context API (no Redux/Zustand):
| Context | Purpose |
|---------|---------|
| `AuthContext` | MSAL + user info + role detection (global/tenant admin, operator, viewer, MCP access, bootstrap manager) |
| `SignalRContext` | WebSocket connection, group subscriptions, auto-reconnect |
| `TenantContext` | Current tenant ID (persisted to localStorage) |
| `NotificationContext` | Toast notifications with auto-dismiss + deduplication |
| `GlobalNotificationContext` | In-app persistent notifications for Global Admins |
| `SidebarContext` | Sidebar expanded/collapsed state |
| `ThemeContext` | Dark mode toggle (localStorage) |
### API Communication
- `lib/authenticatedFetch.ts` – Wraps `fetch()` with Bearer token, 401 retry with token refresh
- `lib/api.ts` – Typed URL builder for all backend endpoints
- `hooks/useAuthenticatedFetch.ts` – React hook with loading/error state
- Tenant isolation: All endpoints append `?tenantId={id}`
### Key UI Components
| Component | Location | Purpose |
|-----------|----------|---------|
| `GlobalSidebar` | `components/` | Main navigation sidebar |
| `Navbar` | `components/` | Top navigation bar |
| `GlobalSearch` | `components/` | Cross-session search overlay |
| `ProtectedRoute` | `components/` | Auth guard with role-based access |
| `FeedbackBubble` | `components/` | In-app feedback button |
| `SessionTable` | `dashboard/components/` | Paginated, filterable, sortable session list |
| `EventTimeline` | `sessions/[id]/components/` | Phase-grouped event visualization |
| `PhaseTimeline` | `sessions/[id]/components/` | Visual phase progress with live activity |
| `VulnerabilityReportSection` | `sessions/[id]/components/` | CVE findings display |
| `AnalysisResultsSection` | `sessions/[id]/components/` | Analyze rule results |
| `ScriptExecutions` | `components/` | Script output viewer |
| `PerformanceChart` | `components/` | Time-series metrics chart |
| `ValidationIndicator` | `components/` | Device validation status |
| `AppInsightsInit` | `components/` | Azure Application Insights initialization |
### UI Patterns
- Settings pages use `error` (string|null) + `successMessage` (string|null) state for notifications
- Notifications rendered at top of `<main>` content area
- `"use client"` directive on interactive components
- Expand/collapse sections with guard blocks
- `UnsavedChangesModal` prevents navigation with unsaved form state
---
## Shared Library
**Target:** .NET Standard 2.0 (compatible with both net48 Agent and net8.0 Backend)
### Model Categories
| Namespace | Key Classes | Purpose |
|-----------|-------------|---------|
| `Models/Enrollment/` | `EnrollmentEvent`, `SessionRegistration`, `EnrollmentPhase`, `EventSeverity`, `BootstrapSession` | Core enrollment data |
| `Models/Config/` | `AgentConfigResponse`, `CollectorConfiguration`, `AnalyzerConfiguration`, `TenantConfiguration`, `AdminConfiguration`, `McpAccessPolicy`, `PlanTierDefinition` | Configuration hierarchy |
| `Models/Rules/` | `GatherRule`, `AnalyzeRule`, `ImeLogPattern`, `RuleResult` | Rules engine |
| `Models/Metrics/` | `UsageMetrics`, `UsageMetricsSnapshot`, `PlatformStats`, `AppInstallSummary` | Analytics |
| `Models/Diagnostics/` | `DiagnosticsLogPath`, `AgentErrorReport`, `DistressReport` | Diagnostics & error reporting |
| `Models/Notifications/` | `NotificationAlert`, `NotificationSeverity`, `WebhookProviderType` | Notification system |
| `DataAccess/` | 13 repository interfaces (`ISessionRepository`, `IConfigRepository`, etc.) | Data access contracts |
| `ApiModels.cs` | Request/Response pairs for all endpoints | API contracts |
| `Constants.cs` | Table names (33), API endpoints, event types (50+), defaults | Shared constants |
### Key Enums
- **`EnrollmentPhase`**: Unknown(-1), Start(0), DevicePreparation(1), DeviceSetup(2), AppsDevice(3), AccountSetup(4), AppsUser(5), FinalizingSetup(6), Complete(7), Failed(99)
- **`EventSeverity`**: Trace(-1), Debug(0), Info(1), Warning(2), Error(3), Critical(4)
- **`WebhookProviderType`**: None(0), TeamsLegacyConnector(1), TeamsWorkflowWebhook(2), Slack(10)
- **50+ Event Types**: `phase_transition`, `app_install_completed`, `enrollment_complete`, `enrollment_failed`, `esp_state_change`, `performance_snapshot`, `script_completed`, `gather_result`, `software_inventory_analysis`, etc.
---
## Data Model
### Azure Table Storage (33 Tables)
| Table | PartitionKey | RowKey | Purpose |
|-------|-------------|--------|---------|
| `Sessions` | TenantId | SessionId | Enrollment sessions |
| `SessionsIndex` | TenantId | IndexKey | Session search indexes |
| `Events` | SessionId | Timestamp_Sequence | Individual events |
| `EventTypeIndex` | TenantId | EventType_Timestamp | Event type search index |
| `AdminConfiguration` | "GlobalConfig" | "config" | Platform-wide settings |
| `TenantConfiguration` | TenantId | "config" | Per-tenant settings |
| `GatherRules` | TenantId | RuleId | Data collection rules |
| `AnalyzeRules` | TenantId | RuleId | Issue detection rules |
| `ImeLogPatterns` | TenantId | PatternId | IME log regex patterns |
| `RuleResults` | TenantId | SessionId_RuleId | Analysis findings |
| `RuleStates` | TenantId | RuleId | Rule enable/disable state |
| `UsageMetrics` | TenantId | MetricDate | Daily usage snapshots |
| `AppInstallSummaries` | TenantId | SessionId_AppName | Per-app install data |
| `PlatformStats` | "Global" | "stats" | Cumulative platform stats |
| `AuditLogs` | TenantId | Timestamp_Id | Admin action audit trail |
| `UserActivity` | TenantId | UserId | User login tracking |
| `UserUsageLog` | TenantId | UserId_Timestamp | Per-user API usage tracking |
| `BootstrapSessions` | TenantId / "CodeLookup" | ShortCode | OOBE bootstrap tokens |
| `BlockedDevices` | TenantId | SerialNumber | Blocked devices |
| `BlockedVersions` | "BlockedVersions" | Pattern | Version block/kill rules |
| `SessionReports` | TenantId | ReportId | User-submitted reports |
| `DistressReports` | TenantId | Timestamp_Id | Pre-auth distress signals |
| `GlobalAdmins` | "GlobalAdmins" | UPN | Platform-level admins |
| `TenantAdmins` | TenantId | UPN | Tenant-level admins (Admin, Operator, Viewer) |
| `McpUsers` | "McpUsers" | UPN | MCP API users |
| `PreviewWhitelist` | "Preview" | TenantId | Preview access gate |
| `PreviewConfig` | "Preview" | "config" | Preview feature config |
| `GlobalNotifications` | "GlobalNotifications" | InvertedTicks_Id | Persistent in-app notifications for Global Admins |
| `DeviceSnapshot` | TenantId | DeviceSerial | Device hardware/network snapshots |
| `VulnerabilityCache` | CacheType | CacheKey | Cached NVD/MSRC/KEV CVE data |
| `VulnerabilityReports` | TenantId | SessionId | Per-session vulnerability findings |
| `SoftwareInventory` | TenantId | SoftwareName | Aggregated software inventory |
| `CveIndex` | CveId | TenantId_SessionId | CVE → session cross-reference |
### Azure Blob Storage
- **Diagnostics container**: Agent-uploaded ZIP packages (`AgentDiagnostics-{sessionId}-{ts}.zip`)
- **Session reports container**: User-submitted report ZIPs
- **Platform stats blob**: Cached JSON for landing page
### Entity Relationships
```
Session (1) ──► (N) EnrollmentEvent
Session (1) ──► (N) RuleResult
Session (1) ──► (N) AppInstallSummary
Session (1) ──► (0..1) SessionReport
Session (1) ──► (0..1) VulnerabilityReport
Session (N) ◄── (1) TenantConfiguration
TenantConfiguration (N) ◄── (1) AdminConfiguration (inherits defaults)
GatherRule ──► (agent executes) ──► EnrollmentEvent (gather_result)
AnalyzeRule ──► (backend evaluates) ──► RuleResult
ImeLogPattern ──► (agent matches) ──► EnrollmentEvent (various types)
SoftwareInventory ──► VulnerabilityCorrelation ──► VulnerabilityReport
```
---
## Security Architecture
### Authentication Layers
**Agent → Backend (device auth):**
1. **MDM Client Certificate** (primary)
- Agent discovers Intune MDM cert in LocalMachine\My store
- Sent via TLS (mTLS), forwarded as `X-ARR-ClientCert` by Azure App Service
- Validated against embedded Intune intermediate + root CA certificates
2. **Bootstrap Token** (pre-MDM, OOBE)
- Admin creates time-limited token+code via web UI
- Agent sends as `X-Bootstrap-Token` header
- Bypasses cert/rate/hardware validation
3. **Hardware Headers** (supplementary)
- `X-Device-SerialNumber`, `X-Device-Manufacturer`, `X-Device-Model`
- Used for whitelist validation and device identification
4. **Agent Version Header**
- `X-Agent-Version` – used for version block enforcement
**Web → Backend (user auth):**
- Microsoft Entra ID multi-tenant JWT via `AuthenticationMiddleware`
- Dynamic OIDC metadata per tenant (cached 24h)
- Claims: `tid` (tenant), `upn` (user), `oid` (object ID)
**MCP → Backend:**
- JWT auth + `X-Client-Source: mcp` header for usage tracking
- Access controlled via `McpUserService`
### Validation Pipeline (per agent request)
```
ValidateSecurityAsync() (SecurityValidationExtensions.cs)
├─ 1. Tenant existence & suspension check (cheapest first)
├─ 2. Bootstrap token gate (if present → short-circuit)
├─ 3. Certificate validation against Intune CA chain
├─ 4. Rate limiting (sliding window, 1-min, per-device)
├─ 5. Hardware whitelist check (optional per tenant)
└─ 6. Autopilot device validation via MS Graph (optional per tenant)
```
### Agent Binary Integrity Verification
Agent downloads are verified using SHA-256 hashes through two independent channels:
**Channel 1: version.json (Blob Storage)**
- CI/CD computes SHA-256 of the agent ZIP after build
- Hash is written to `version.json`: `{ "version": "1.0.x", "sha256": "..." }`
- Bootstrapper and Self-Updater verify the downloaded ZIP against this hash
**Channel 2: Backend Hash-Oracle (AdminConfiguration)**
- CI/CD writes the SHA-256 hash to `AdminConfiguration.LatestAgentSha256` in Table Storage
- Backend delivers the hash via `AgentConfigResponse.LatestAgentSha256`
- Self-Updater uses the backend hash with priority over the version.json hash
- Separate trust channel: an attacker would need to compromise both Blob Storage AND the backend
**Verification flow:**
```
Self-Updater:
1. Fetch version.json → get sha256 field
2. Download ZIP
3. Verify SHA-256: backend hash (priority) > version.json hash > skip (backward compat)
Bootstrapper:
1. Fetch version.json → get sha256 field
2. Download ZIP
3. Verify SHA-256: version.json hash > legacy Content-MD5 header > skip (backward compat)
```
### Authorization Model
#### Endpoint Access Policies (EndpointAccessPolicyCatalog)
Six policy tiers (fail-closed: unregistered routes get 403). Each entry also specifies a `TenantScoping` mode (see [Tenant Data Isolation](#tenant-data-isolation-centralized-enforcement)):
| Policy | Description |
|--------|-------------|
| `PublicAnonymous` | No auth required |
| `DeviceOrBootstrapAuth` | mTLS cert or bootstrap token (via `ValidateSecurityAsync`) |
| `AuthenticatedUser` | Valid JWT, any tenant |
| `MemberRead` | Admin + Operator + Viewer roles |
| `TenantAdminOrGA` | Tenant Admin or Global Admin |
| `BootstrapManagerOrGA` | Bootstrap Manager permission or Global Admin |
| `GlobalAdminOnly` | Platform-wide admin |
#### Roles
| Role | Scope | Capabilities |
|------|-------|-------------|
| **Global Admin** | Platform-wide | Global config, all tenants, platform metrics, health checks, MCP users |
| **Tenant Admin** | Single tenant | Tenant config, rules, admin management, device blocking, notifications |
| **Operator** | Single tenant | Write access, optionally Bootstrap Manager permission |
| **Viewer** | Single tenant | Read-only dashboard, session detail view |
### Tenant Data Isolation (Centralized Enforcement)
Tenant isolation is enforced centrally in the middleware pipeline via `TenantScoping` on every catalog entry:
| Scoping Mode | Source | Middleware Behavior |
|-------------|--------|---------------------|
| `None` | N/A | No tenant check (public, device-auth, global-admin-only routes) |
| `Jwt` | JWT `tid` claim | Inherently safe — tenant derived from token, no cross-tenant check needed |
| `RouteParam` | `{tenantId}` in route | Middleware extracts from path, enforces JWT tenant == route tenant (Global Admins exempt) |
| `QueryParam` | `?tenantId=` query | Middleware extracts from query string (falls back to JWT tenant), enforces cross-tenant check |
**How it works:**
1. `PolicyEnforcementMiddleware` resolves the `EndpointPolicyEntry` for every request
2. For `RouteParam`/`QueryParam` scoping: extracts the target tenant ID from the request
3. Compares target tenant against JWT `tid` claim — rejects with 403 if mismatched (unless Global Admin)
4. Sets `RequestContext.TargetTenantId` for downstream use by function handlers
5. Fail-closed: unregistered routes get 403 automatically
**Additional layers:**
- All Table Storage queries filtered by `PartitionKey = TenantId`
- SignalR groups: `tenant-{tenantId}` for scoped broadcasts
- `CrossTenantAccessTests` validates that every tenant-scoped endpoint correctly rejects cross-tenant access
---
## Real-Time Communication
### SignalR Integration
- **Hub:** `autopilotmonitor`
- **Transport:** WebSocket with auto-reconnect (0s, 2s, 10s, 30s backoff)
- **Token factory:** Fresh JWT per connection attempt
### Groups & Events
| Group | Events | Triggered By |
|-------|--------|-------------|
| `tenant-{tenantId}` | `newSession`, `sessionStatusChanged`, `eventReceived`, `ruleResultReceived` | Event ingestion, session registration |
| `global-admins` | `newSession` (all tenants), platform stats updates | Cross-tenant broadcasts |
### Client-Side (SignalRContext)
- Auto-rejoin groups after reconnect
- Components subscribe on mount, unsubscribe on unmount
- Connection state exposed for UI indicators
---
## Rules Engine
### Three Rule Types
| Type | Execution | Purpose |
|------|-----------|---------|
| **Gather Rules** | Agent-side | Collect data from registry, WMI, event logs, files, commands |
| **Analyze Rules** | Backend-side | Detect issues in enrollment events with confidence scoring |
| **IME Log Patterns** | Agent-side | Parse IME/AppWorkload/HealthScripts logs with regex |
### Gather Rules
- **Collector Types:** `registry`, `wmi`, `eventlog`, `file`, `command_allowlisted`, `logparser`, `json`, `xml`
- **Triggers:** `startup`, `interval`, `phase_change`, `on_event`
- **Security:** Command allowlist enforced by `GatherRuleGuards`
- **Output:** Emits `gather_result` event with collected data
### Analyze Rules
- **Conditions:** Match events by source, signal, operator, value, with event correlation
- **Confidence Scoring:** `BaseConfidence` + `ConfidenceFactors` (signal × weight), threshold at 40
- **Output:** `RuleResult` with explanation, remediation steps, related docs
- **Trigger:** Evaluated after enrollment completion/failure
### IME Log Patterns
- **Categories:** `always` (all phases), `currentPhase`, `otherPhases`
- **Actions:** 50+ (e.g., `setCurrentApp`, `updateStateInstalled`, `espPhaseDetected`)
- **Regex:** Named capture groups, `{GUID}` placeholder for Intune policy IDs
- **Tracked Logs:** `IntuneManagementExtension*.log`, `AppWorkload*.log`, `AgentExecutor*.log`, `HealthScripts*.log`
### Rule Distribution
1. Individual JSON files in `rules/gather/`, `rules/analyze/`, `rules/ime-log-patterns/`
2. GitHub Actions validates against JSON Schema + combines into `rules/dist/`
3. Combined files embedded as resources in Functions assembly
4. Served to agents via `/api/agent/config`, manageable via web UI per tenant
5. `ReseedFromGitHub` function can re-fetch from GitHub on demand
---
## Session Lifecycle
### Three Completion Paths
```
Path 1: IME Pattern Completion
IME logs show all apps completed → Hello wait → enrollment_complete
Path 2: ESP Exit + Hello (Composite)
ESP final exit (event 62407) → Hello wait (300s) → enrollment_complete
Path 3: Desktop Arrival (No-ESP / WDP v2)
explorer.exe detected under real user → Hello wait → enrollment_complete
```
### ESP & Hello Tracking
- **ESP Events:** Shell-Core event log (62404=Hello wizard start, 62407=ESP exit/WhiteGlove)
- **Hello Events:** User Device Registration log (300=NGC success, 301=NGC failure)
- **Hello Wait:** 30s for wizard start → 300s for completion → timeout
- **HelloOutcome:** Tracked property recording Hello result
- **Policy Check:** WHfB policy registry poll every 10s; skip Hello wait if not configured
### Failure Detection
- **Terminal failures** (Failure, Abort, WhiteGlove_Failed) → immediate `enrollment_failed`
- **Recoverable failures** (Timeout) → 60s grace period before marking failed
- **Auth failures** → circuit breaker (max 5 attempts or configurable timeout)
- **Device-Only ESP:** 5-min timer after DeviceSetup exit; if no AccountSetup → device-only classification
- **WDP v2 gate skip:** `_enrollmentType == "v2"` → desktop arrival gate skipped (no ESP in WDP v2)
### WhiteGlove (Pre-Provisioning)
- Part 1: `whiteglove_complete` → persist state, exit gracefully (no self-destruct, session preserved)
- Part 2: Agent restarts on next boot, detects `whiteglove.complete` marker → `whiteglove_resumed`
- Session survives across reboot; sequence counter persisted
### State Persistence (Crash Recovery)
| File | Purpose |
|------|---------|
| `enrollment-state.json` | ESP flags, Hello state, completion signals |
| `ime-tracker-state.json` | Phase order, seen apps, file positions |
| `session.id` / `session.seq` | Session identity + event sequence counter |
| `enrollment-complete.marker` | Cleanup retry flag if previous cleanup failed |
### Signal Audit Trail
Terminal events (`enrollment_complete`/`enrollment_failed`) include `signalsSeen` and `signalTimestamps` for full state machine transparency. Completion check events are throttled (1x/min/source) with full state machine snapshot.
### Cleanup & Self-Destruct
1. Stop collectors, run shutdown analyzers
2. Drain event spool, final upload
3. Upload diagnostics ZIP (if configured)
4. Remove Scheduled Task, delete binaries/config/spool
5. Optionally reboot device (`RebootOnComplete`)
---
## Configuration Hierarchy
```
AdminConfiguration (global, single row in Azure Table)
│ GlobalRateLimitRequestsPerMinute (default 100)
│ CollectorIdleTimeoutMinutes (default 15)
│ AgentMaxLifetimeMinutes (default 360)
│ DiagnosticsGlobalLogPathsJson
│ LatestAgentSha256
│
└──► TenantConfiguration (per tenant, inherits/overrides)
│ Rate limiting (override or inherit global)
│ Hardware whitelist, Autopilot device validation
│ Collector intervals, Hello timeout
│ Diagnostics: UploadEnabled, LogPathsJson
│ Auth circuit breaker settings
│ Teams/Slack/Telegram notifications
│ Bootstrap token enablement
│ UnrestrictedMode
│
└──► AgentConfigResponse (delivered to agent via /api/agent/config)
│ ConfigVersion (currently 21)
│ CollectorConfiguration (nested)
│ AnalyzerConfiguration (nested)
│ GatherRules[] (merged built-in + tenant)
│ ImeLogPatterns[] (merged built-in + tenant)
│ DiagnosticsLogPaths[] (merged global + tenant)
│ LatestAgentSha256
└ Various flags and intervals
```
**Caching:** Both admin and tenant configs cached 5 minutes in-memory (`IMemoryCache`).
---
## Diagnostics & Upload
### Architecture (Post-Refactor)
- **Old:** Long-lived SAS URL stored in agent config → device stores in `remote-config.json`
- **New:** `DiagnosticsUploadEnabled` boolean; agent calls `POST /api/agent/upload-url` just before upload
- SAS URL never stored on device or in config, kept in memory only
### Upload Flow
1. Agent creates ZIP: `sessioninfo.txt` + agent logs + IME logs + configured paths
2. Agent calls `POST /api/agent/upload-url` → receives short-lived SAS URL (1h)
3. Agent uploads via `PUT {blobUrl}` with `x-ms-blob-type: BlockBlob`
4. 3 retries with exponential backoff (2s, 4s, 8s); 401/403 = non-retryable
### Diagnostics Log Paths
- Global paths: `AdminConfiguration.DiagnosticsGlobalLogPathsJson` (built-in, `IsBuiltIn=true`)
- Tenant paths: `TenantConfiguration.DiagnosticsLogPathsJson` (custom, `IsBuiltIn=false`)
- Merged list delivered to agent; security validated by `DiagnosticsPathGuards`
### Upload Modes
| Mode | Behavior |
|------|----------|
| `Off` (default) | Disabled |
| `Always` | Upload on both success and failure |
| `OnFailure` | Upload only on enrollment failure |
---
## Vulnerability Management
### Architecture
Software inventory collected by agent → correlated with CVE databases on backend → vulnerability reports per session.
### Data Sources
| Source | Service | Purpose |
|--------|---------|---------|
| NIST NVD | `NvdApiClient` | CVE database with CPE matching |
| Microsoft MSRC | `MsrcApiClient` | Microsoft-specific security advisories |
| CISA KEV | `KevDataService` | Known Exploited Vulnerabilities feed |
### Flow
1. `SoftwareInventoryAnalyzer` captures installed software at agent start+shutdown
2. Events uploaded via normal ingest pipeline
3. `VulnerabilityCorrelationService` matches software against CVE data using CPE identifiers
4. `VersionComparer` handles semantic version comparison for affected version ranges
5. Results stored in `VulnerabilityReports` table, viewable in session detail UI
6. Custom CPE mappings configurable via admin UI for unmatched software
7. `VulnerabilityDataSyncFunction` periodically refreshes CVE cache
### Search
Sessions can be searched by CVE ID via `GET /api/search/sessions-by-cve`, backed by the `CveIndex` table.
---
## Notification System
### Webhook Notifications
Enrollment completion/failure events trigger webhook notifications via `WebhookNotificationService`.
| Provider | Renderer | Format |
|----------|----------|--------|
| Teams Legacy Connector | `LegacyTeamsConnectorRenderer` | Office 365 Connector card |
| Teams Workflow Webhook | `TeamsWorkflowAdaptiveCardRenderer` | Adaptive Card |
| Slack | `SlackRenderer` | Block Kit |
Configuration per tenant via `TenantConfiguration.WebhookUrl` + `WebhookProviderType`.
**Rule Results in Notifications:** `NotificationAlertBuilder.AddRuleResultSections()` appends significant analyze rule findings (warning/high/critical severity only, max 5) to enrollment completion/failure webhook alerts — giving admins immediate visibility into detected issues without visiting the portal.
### Telegram Notifications
`TelegramNotificationService` sends enrollment notifications via Telegram bot API. Configured per tenant.
### Email Notifications
`ResendEmailService` handles transactional emails via Resend.com API (used for Preview notifications).
### In-App Notifications
`GlobalNotificationService` provides persistent notifications for Global Admins, stored in `GlobalNotifications` table. Survives page reloads, dismissable per user.
---
## Testing
### Backend Tests (`src/Backend/AutopilotMonitor.Functions.Tests/`)
**Target:** .NET 8.0, xUnit
| Test File | Coverage |
|-----------|---------|
| `IngestCriticalPathTests` | Regression guard for `StampServerFields()` — ensures `ReceivedAt`, `TenantId`, `SessionId` are stamped |
| `SecurityValidatorTests` | Certificate validation, rate limiting, hardware whitelist, device validation flows |
| `DistressValidationTests` | Distress report validation |
| `DistressRateLimitServiceTests` | Distress rate limiting |
| `EventTimestampValidationTests` | Timestamp clamping and validation |
| `NdjsonParserTests` | NDJSON parsing |
| `BuiltInRulesTests` | Built-in rule logic |
| `EndpointPolicyCatalogCompletenessTests` | Ensures every HTTP route has a catalog entry |
| `CrossTenantAccessTests` | Validates tenant-scoped endpoints reject cross-tenant access |
| `ODataSanitizerTests` | OData query sanitization |
| `SsrfGuardTests` | SSRF protection validation |
### Agent Tests (`src/Agent/AutopilotMonitor.Agent.Core.Tests/`)
**Target:** .NET Framework 4.8, xUnit
| Test File | Coverage |
|-----------|---------|
| `DesktopArrivalDetectorTests` | Desktop arrival detection |
| `DiagnosticsPathGuardsTests` | Path guard validation |
| `GatherRuleGuardsTests` | Gather rule path/target guards |
| `AppPackageStateListTests` | App package state tracking |
| `AppPackageStateTests` | Individual package state machine |
| `CmTraceLogParserTests` | CMTrace log format parsing |
| `EnrollmentStatePersistenceTests` | State file persistence |
| `LogFilePositionTrackerTests` | Log file tail position bookmarking |
---
## Infrastructure & Deployment
### Azure Resources
| Resource | Purpose |
|----------|---------|
| Azure Functions (Isolated Worker) | REST API, event processing, timer triggers |
| Azure Table Storage | 33 tables for sessions, events, config, rules, metrics, vulnerability data |
| Azure Blob Storage | Diagnostics ZIPs, session reports, platform stats cache, agent binaries |
| Azure SignalR Service | WebSocket hub for real-time updates |
| Azure Static Web Apps | Next.js frontend hosting |
| Azure Container App | MCP server (provisioned via `infra/mcp-server.bicep`) |
| Azure Container Registry | MCP server container images |
| Application Insights | Logging, telemetry, performance monitoring |
| Microsoft Entra ID | Multi-tenant OIDC authentication |
### MCP Server
Provisioned via Bicep (`infra/mcp-server.bicep`):
- Azure Container Registry (Basic SKU)
- Log Analytics Workspace
- Container App Environment
- Container App (`autopilotmonitor-mcp`) running from ACR image
### Bootstrap Deployment
`scripts/Bootstrap/Install-AutopilotMonitor.ps1` — Intune Platform Script (.ps1):
- Deployed early in Autopilot enrollment via Intune
- Downloads agent ZIP from blob storage, verifies SHA-256 integrity
- Runs `AutopilotMonitor.Agent.exe --install`
- Multi-signal guard: registry marker, OOBE state, WMI/filesystem user profile, 12h bootstrap window (prevents ghost sessions)
- `Test-ShouldBootstrapAgent.ps1` — standalone test for guard logic
- `Uninstall-AutopilotMonitor.ps1` — manual cleanup
### Agent Build & Upload
`scripts/Deployment/build_and_upload_release_agent_build.ps1`:
- Builds agent solution in Release mode
- Creates ZIP package
- Computes SHA-256 hash
- Uploads ZIP + `version.json` (with hash) to Azure Blob Storage
### CI/CD (GitHub Actions)
| Workflow | Trigger | Purpose |
|----------|---------|---------|
| `azure-static-web-apps-*.yml` | Push to main (Web changes) | Build + deploy Next.js to Azure Static Web Apps |
| `combine-rules.yml` | Changes to `rules/`, `schema/`, `scripts/` | Validate rules against JSON Schema, combine into `dist/`, auto-commit |
### Environment Variables (Web)
```
NEXT_PUBLIC_ENTRA_CLIENT_ID # App registration client ID
NEXT_PUBLIC_ENTRA_REDIRECT_URI # Auth redirect (localhost:3000 dev)
NEXT_PUBLIC_ENTRA_POST_LOGOUT_REDIRECT_URI
NEXT_PUBLIC_API_BASE_URL # Backend URL (localhost:7071 dev)
NEXT_PUBLIC_PLATFORM_STATS_MANIFEST_URL
```
---
## Build & Development
### Build Commands
```bash
# .NET solution (Agent + Backend + Shared)
dotnet build AutopilotMonitor.sln --nologo -v quiet
# Backend tests
dotnet test src/Backend/AutopilotMonitor.Functions.Tests/
# Agent tests
dotnet test src/Agent/AutopilotMonitor.Agent.Core.Tests/
# Web frontend
cd src/Web/autopilot-monitor-web
npm install
npm run dev # Development server (localhost:3000)
npm run build # Production build
npx tsc --noEmit # Type checking only
# Rules validation + combine
cd rules && node scripts/combine.js
```
### Agent CLI Arguments
```
--install Deploy via Scheduled Task (Intune package)
--run-gather-rules One-shot data collection, then exit
--run-ime-matching Offline IME log parsing, write ime_pattern_matching.log
--console Enable console output
--log-level {level} Set log level (debug/info/warning/error)
--api-url {url} Override API endpoint (alias: --backend-api)
--bootstrap-token {token} Pre-MDM bootstrap auth
--ime-log-path {path} Override IME log folder
--ime-match-log {path} Write matched IME log lines to file (debug)
--replay-log-dir {path} Enable log replay mode
--replay-speed-factor {n} Compression factor (default 50)
--no-cleanup Disable self-destruct
--reboot-on-complete Trigger reboot after enrollment
--new-session Start fresh session
--keep-logfile Preserve logs after cleanup
--await-enrollment Wait for MDM certificate before starting
--await-enrollment-timeout {min} MDM cert wait timeout (default 480min)
--disable-geolocation Skip geo-location detection
```
### Key Design Conventions
| Convention | Details |
|------------|---------|
| Agent endpoint security | All agent endpoints use `req.ValidateSecurityAsync()` from `SecurityValidationExtensions.cs` |
| Route policy catalog | Every HTTP route MUST be registered in `EndpointAccessPolicyCatalog` (fail-closed → 403) |
| ConfigVersion | Tracks agent capability level (currently 21 = NTP time check + timezone auto-set) |
| Phase progression | Forward-only: DeviceSetup(1) → AccountSetup(2), no backward transitions |
| Phase isolation | App IDs seen in earlier phases are ignored in later phases (IME tracker) |
| Completion throttling | Max 1 `completion_check` event per source per minute |
| Sequence persistence | Saved every 50 events + on critical events; crash recovery uses spool ceiling |
| Settings UI | `error` + `successMessage` state for notifications at top of `<main>` |
| Maintenance timer | Runs every 2 hours (not daily, despite function name) |
| Agent versioning | Auto-incremented: 1.0.{BuildNumber} |
| Timestamp clamping | Preserves originals + flags for troubleshooting |
| Bootstrap scripts | Must be pure ASCII (no em-dashes/Unicode) — PS 5.1 compatibility |