Commit Graph

57 Commits

Author SHA1 Message Date
Oliver Kieselbach 42ff4284d8 added version info to backend 2026-04-22 19:36:19 +02:00
Oliver Kieselbach 8e39299da5 feat(backend): M5.d.3 queue-triggered index reconcile consumer
Consumes the telemetry-index-reconcile queue (M5.d.2) and fans each
envelope out into 0–3 index-table rows via IIndexTableRepository
(M5.d.1). Closes the producer→consumer loop; once the
EnableIndexDualWrite flag is flipped, primary commits drive
eventual-consistent index upserts asynchronously.

Components:
- Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues 5.5.4
  package for the isolated-worker QueueTrigger binding.
- IndexReconcileHandler (plain class — not interface-abstracted per
  Plan §M5.d design: DAL abstraction lives at IIndexTableRepository,
  not here). Routing:
    Signal → SignalsByKind (always, 1 row).
    DecisionTransition:
      IsTerminal=true          → SessionsByTerminal
      Taken=true               → SessionsByStage
      Taken=false + reason     → DeadEndsByReason
      ClassifierVerdictId!=null → ClassifierVerdictsByIdLevel
  Up to 3 rows per DecisionTransition envelope (a Taken+terminal+
  classifier transition fans to SessionsByTerminal + SessionsByStage
  + ClassifierVerdictsByIdLevel).
- IndexReconcileFunction thin QueueTrigger wrapper
  (Connection = AzureTableStorageConnectionString). Malformed JSON
  logged + dropped to avoid poison-queue retry loops; transient
  repo errors propagate so the platform retries.
- DI: handler registered as Singleton in Program.cs.

Failure semantics:
- Required-field-missing envelopes (Signal without SessionSignalOrdinal
  or SignalKind; DecisionTransition without StepIndex) → log warn,
  drop. These won't become valid on retry — avoiding the retry loop.
- Transient Table Storage errors → rethrow. QueueTrigger platform
  retries up to 5 times by default, then moves to
  telemetry-index-reconcile-poison for manual investigation.

Deferred to M5.d.4:
- SessionsByStage stale-row pruning: when a session moves from
  Stage-X→Stage-Y, the old Stage-X row stays. Either query-side
  dedupe (per SessionId, max-Ticks RK) or reconcile-timer pruning
  handles this. Deliberately not solved in the handler to avoid
  a query+delete round-trip per Taken transition.
- End-to-end dual-write verification tests (Azurite harness) are
  the M5.d.4 release gate before the flag is flipped.

Tests (+10 in Functions) via capture-only FakeIndexTableRepository:
- Signal happy path (1 SignalsByKind row).
- Signal missing SessionSignalOrdinal / SignalKind → 0 rows (x2).
- DecisionTransition Terminal+Taken+Classifier → 3 rows.
- DecisionTransition Taken non-terminal no-classifier → 1 row
  (SessionsByStage only).
- DecisionTransition dead-end with classifier → 2 rows
  (DeadEndsByReason + ClassifierVerdictsByIdLevel).
- DecisionTransition dead-end without reason → 0 rows (PK-collision
  defense).
- DecisionTransition missing StepIndex → 0 rows (all paths gated).
- Unknown SourceKind → 0 rows.
- Null envelope → 0 rows.

Full Functions suite: 682 green (prev 672 + 10 new). Full solution
build green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 12:03:06 +02:00
Oliver Kieselbach c3ee0cad55 feat(backend): M5.d.2 index-reconcile queue producer + feature flag
Adds the async fan-out producer path from Plan §2.8 / §M5.d so primary
Signals + DecisionTransitions rows can be projected onto their index
tables without blocking the hot ingest loop.

Components:
- Constants.QueueNames.TelemetryIndexReconcile (azure-queue-name-safe,
  all lowercase + hyphens).
- IndexReconcileEnvelope (Shared DTO, flat shape with nullable fields
  per SourceKind="Signal"/"DecisionTransition"). Avoids polymorphic
  Newtonsoft deserialization — see M5.c.3 finding.
- IndexReconcileEnvelopeFactory pure static helper (FromSignal /
  FromDecisionTransition / BuildBatch) — state-less projection.
- IIndexReconcileProducer (Shared interface) contract: no-op when
  the feature flag is off; never rethrows underlying-queue failures.
- AzureQueueIndexReconcileProducer (Functions) — Base64-encoded JSON
  messages, lazy CreateIfNotExistsAsync guarded by Interlocked flag,
  Managed-Identity-or-connection-string construction mirroring
  BlobStorageService / TableStorageService.
- IngestTelemetryFunction ctor gains IIndexReconcileProducer; after
  primary Signals + DecisionTransitions commit it builds envelopes
  and enqueues them. Event path is untouched (events don't feed the
  index tables per §2.8).
- Program.cs registers the producer as a singleton.

Feature gate (AdminConfiguration.EnableIndexDualWrite, default false):
- New bool field in AdminConfiguration.
- TableConfigRepository Store + Map extension (per
  feedback_table_storage_serialization).
- Producer checks config each call via AdminConfigurationService's
  5-min memory cache. Flag off → primary behaviour is bit-exact.

Fault tolerance: queue-side exceptions (network, throttling, missing
queue before first CreateIfNotExists) are logged per envelope and
swallowed. The primary row is the source of truth; the 2h reconcile
timer (M5.d.4) is the safety-net recovery path.

Tests (+11 in Functions):
- 5 IndexReconcileEnvelopeFactoryTests: Signal/DecisionTransition
  projection, dead-end preservation, batch ordering, null/empty.
- 3 IndexReconcileEnvelopeSerializationTests: Newtonsoft round-trip
  (Signal, full DecisionTransition, minimal-envelope tolerance).
- 3 AdminConfigurationEnableIndexDualWriteTests: default-false on
  new config + CreateDefault, persists when set.

Deferred to later sub-milestones:
- M5.d.3: queue-triggered consumer that maps envelopes to 0–3 index
  rows using M5.d.1 TableIndexRepository.
- M5.d.4: 2h reconcile timer + release-gate dual-write verification
  tests, then flip EnableIndexDualWrite=true in prod admin config.

Full Functions suite: 672 green (prev 661 + 11 new). Full solution
build green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 11:48:27 +02:00
Oliver Kieselbach d218a6ca25 feat(backend): M5.b.2 EventIngestProcessor — V2 endpoint event-pipeline parity
Extract the post-parse event-processing tail of IngestEventsFunction into a
standalone EventIngestProcessor service so /api/agent/telemetry runs the same
downstream logic as /api/agent/ingest. Deliberate copy-duplication, not a
shared refactor:

- Legacy /api/agent/ingest is production-hot — it serves live traffic from
  every deployed V1 agent and is NOT touched by this commit. Zero regression
  risk on the existing path.
- EventIngestProcessor is a near-verbatim copy of the legacy pipeline's tail
  (~1000 LoC). Duplication is accepted because legacy /api/agent/ingest is
  scheduled for decommission post-v11 GA (see tasks/todo.md → Follow-Ups →
  Legacy-Agent-Ausmusterung). Until then: bugs fixed in legacy must be ported
  here manually.

Copied pipeline steps (all 1:1 with legacy ProcessIngestAsync lines 150-566):
  StampServerFields → SanitizeEventTimestamps → StoreEventsBatchAsync →
  Index updates fire-and-forget → IME agent version history → ClassifyEvents
  → App install summaries → Geo-location update → UpdateSessionStatusAsync
  (all 7 status branches) → IncrementSessionEventCountAsync → Rule engine
  analysis fire-and-forget + SignalR + stats → Vulnerability correlation with
  WhiteGlove Part-2 merge → IncrementPlatformStatAsync TotalEvents +
  SuccessfulEnrollments → RecordGatherRuleStatsAsync → Diagnostics blob
  update → GetSession for updatedSession → Session age 4h warning → WhiteGlove
  IsPreProvisioned error log → SendWebhookNotificationsAsync (all 3 alert
  paths) → SLA breach evaluation → Admin override detection → ServerAction
  fetch-and-clear with TrackEvent telemetry → BuildSignalRMessages.

Shared (not duplicated, to avoid drift in pure helpers):
  IngestEventsFunction.StampServerFields, SanitizeEventTimestamps,
  RecalculateAppDurations (all internal static, state-less, stable). The
  internal DTOs EventClassification + AppInstallAggregationState are also
  reused — they carry no behaviour, only fields.

IngestTelemetryFunction wiring:
- Return type HttpResponseData → IngestEventsOutput so the V2 endpoint can
  emit SignalR messages for the real-time UI push (matches legacy binding).
- PersistItemsAsync partitions the batch: Events go through the processor
  (full pipeline + AdminAction + pending ServerActions + SignalR); Signal +
  Transition batches skip the processor and read control signals directly
  via a lean ReadControlSignalsAsync helper on ISessionRepository — no point
  firing the full pipeline for a batch that carries no events.
- EventIngestProcessor registered in Program.cs DI.

Known gap (explicitly NOT in this commit): the V2 endpoint lacks a payload
size guard (legacy reads MaxNdjsonPayloadSizeMB from tenant config in
ParseNdjsonRequest). Different payload shape (JSON array vs NDJSON), deferred
to a dedicated follow-up.

All 596 backend tests green (unchanged count — copy path has no new tests
yet; the V2 endpoint's behaviour is validated by the legacy test coverage
through the shared-helper surface + the TelemetryPayloadParser tests from
M5.b.1). Integration coverage lands with M7 test-VM smoke.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 10:04:11 +02:00
Oliver Kieselbach a3d37c67ac added more http compression (responses also) 2026-04-15 00:34:18 +02:00
Oliver Kieselbach 6702e1d43b feat: added DevPrepValidator (device association) and restructured agent to distinguish Classic vs v2 autopilot flow, in addition option for dev build added 2026-04-14 16:09:32 +02:00
Oliver Kieselbach c415872e46 feat: SLA tracking dashboard with notifications 2026-04-13 20:19:24 +02:00
Oliver Kieselbach e9fcb85797 feat: added LatestVersions api and bootstrapper version handling and hints (badge, docs) 2026-04-09 19:50:13 +02:00
Oliver Kieselbach 536d275f32 feat: non-whitelisted hardware list and optional alerts 2026-04-04 20:20:55 +02:00
Oliver Kieselbach d824dd64fd added structured error handler 2026-04-02 22:02:09 +02:00
Oliver Kieselbach 4298223d38 added Ops Alerts 2026-04-02 21:25:27 +02:00
Claude 6f0052be20 Add OpsEvents table for vital infrastructure monitoring
Introduces a new OpsEvents Azure Table Storage table that records vital
infrastructure events (consent flows, maintenance runs, security blocks,
tenant offboarding) visible to Global Admins on the Ops dashboard.

Backend:
- IOpsEventRepository interface + TableOpsEventRepository (PartitionKey=Category)
- OpsEventService with typed helpers (fire-and-forget safe, never throws)
- GetOpsEventsFunction API endpoint (GET /api/global/ops-events)
- Maintenance cleanup with configurable retention (AdminConfig.OpsEventRetentionDays, default 90d)
- Integrated into: ConsentFunction, MaintenanceService, DeviceBlock, VersionBlock, TenantOffboard

Frontend:
- OpsEventsSection component with category filters, severity badges, detail modal
- Configurable retention days directly in the Ops page
- Updated admin/ops page with new events table above existing maintenance trigger

https://claude.ai/code/session_01UCFrxsZ8QYjH7KEYmiGJ6c
2026-04-02 13:23:07 +00:00
Oliver Kieselbach c9f6122c34 fix webhook notification 2026-04-01 03:23:28 +02:00
Oliver Kieselbach d9c4ea2657 agent emergency channel established 2026-03-31 03:07:52 +02:00
Oliver Kieselbach 66cc6af20f added user requests rate limiter, mcp tools bugfix, optimized request pipeline, mcp usage data retention, mcp plantier management 2026-03-29 16:12:15 +02:00
Oliver Kieselbach f6d685392a request telemetry, fixed usage logging bug 2026-03-29 14:06:43 +02:00
Oliver Kieselbach fbea8b5ca4 final CORS fix by using different route 2026-03-28 09:42:28 +01:00
Oliver Kieselbach 49a14c0a00 added CORS middleware 2026-03-28 09:26:44 +01:00
Oliver Kieselbach 8a4a7cf255 Remote MCP server: Streamable HTTP transport, Container App deployment, MCP user access control
- MCP server: stdio → Streamable HTTP (Express) with /health endpoint
- Auth: device code flow → token pass-through (JWT from Claude Code client)
- Dockerfile (multi-stage, node:22-slim) + .dockerignore
- Bicep template: ACR + Container App Environment + Container App (scale-to-zero)
- GitHub Actions: deploy-mcp.yml workflow, all backend workflows migrated to SP auth
- Removed unused dev/main deploy workflows
- Backend: McpAccessPolicy enum (Disabled/WhitelistOnly/AllMembers)
- Backend: McpUsers table + McpUserService (5min cache) + McpUserFunction (CRUD + access check)
- Backend: 6 endpoints registered in EndpointAccessPolicyCatalog
- Web UI: MCP Users management section at /settings/access/mcp-users
- Web UI: user list, add/remove/enable/disable, policy info banner

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 08:09:58 +01:00
Oliver Kieselbach 219f5c686a oauth for mcp server 2026-03-28 06:15:12 +01:00
Oliver Kieselbach cb14e3afbe switch to managed identity 2026-03-28 04:32:34 +01:00
Oliver Kieselbach a6f552a877 added MCP capabilities 2026-03-28 04:20:50 +01:00
Oliver Kieselbach 769beaa14a Refactorings for many reusables, API URL builder, CollectorBaseClass, CorrelationID Middleware, added Polly for external APIs 2026-03-27 11:14:38 +01:00
Claude 36554c5ca4 Add Data Access Layer with repository interfaces and Table Storage implementations
Introduces a clean abstraction layer to decouple domain services from Azure Table Storage,
enabling future migration to Cosmos DB and Event Streaming without touching business logic.

Architecture:
- 10 repository interfaces in Shared/DataAccess/ (ISessionRepository, IRuleRepository, etc.)
- IDataEventPublisher interface with NullDataEventPublisher for future event streaming
- IStorageInitializer for provider-agnostic initialization
- 5 Table Storage implementations that delegate to existing TableStorageService
- DI extension method AddTableStorageDataAccess() with AddEventStreaming<T>() hook
- Migration plan in tasks/todo.md for incremental adoption

https://claude.ai/code/session_01NHWJfPaa8TQHuPzMD7gG63
2026-03-27 06:03:55 +00:00
Oliver Kieselbach 9f7d6e23f1 initial mcp server implementation 2026-03-24 08:06:48 +01:00
Oliver Kieselbach 0fd8ca22d1 renaming global admin 2026-03-23 06:28:59 +01:00
Oliver Kieselbach 26e5925672 feat: Software Inventory Analyzer with Vulnerability Correlation 2026-03-20 14:43:58 +01:00
Oliver Kieselbach 3046d4fb00 small security hardenings 2026-03-19 08:18:53 +01:00
Oliver Kieselbach cc085d5d55 re-worked notification system to support more webhook providers (current, legacyTeamsProvider, TeamsWorkflowProvider, Slack) 2026-03-11 21:59:14 +01:00
Oliver Kieselbach b7f8d3c281 added Galactic notifications in navbar 2026-03-09 22:28:19 +01:00
Oliver Kieselbach eb1e5c6041 added preview email notification service 2026-03-09 19:29:11 +01:00
Oliver Kieselbach b67f1a447e impelented PolicyEnforcementMiddleware 2026-03-08 17:29:32 +01:00
Oliver Kieselbach 0126ce3c93 Added Policy Catalog and Audit for verification (first phase) 2026-03-08 14:23:25 +01:00
Oliver Kieselbach 7fa08c4f8f added API authorization and CLAUDE.md instructions 2026-03-07 21:36:58 +01:00
Oliver Kieselbach 1baff84df0 added raw event timeline and block management for agent versions 2026-03-07 08:33:05 +01:00
Oliver Kieselbach c91fd1a43e massive re-org and re-structuring 2026-03-06 03:02:19 +01:00
Oliver Kieselbach b880f5b534 Added bootstrap endpoints and handling 2026-03-05 18:02:37 +01:00
Oliver Kieselbach 51033cce7b Bootstrap Session implementation 2026-03-05 13:08:14 +01:00
Oliver Kieselbach 20e93aa892 add Corporate Identifier Validation with preview-gated UI
Adds a second device validation method alongside existing Autopilot Device
Validation. Corporate Identifier Validation checks devices against Intune
Corporate Device Identifiers (manufacturer + model + serial number) via the
Graph beta searchExistingIdentities endpoint.

Backend: Extract GraphTokenService from AutopilotDeviceValidator for shared
token/consent logic. Add CorporateIdentifierValidator with caching. Update
SecurityValidator with cascading validation (Autopilot first, Corporate
Identifier fallback). Wire new validator through all agent endpoints.

Frontend: Add second toggle in settings gated behind preview mode. Shared
consent flow skips redirect when consent already exists from other toggle.
Section renamed to "Enrollment Device Validation" in preview mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 10:19:06 +01:00
Oliver Kieselbach eae2898ce2 add Galactic Admin audit logs, audit pagination, and platform metrics backend cache
- Galactic Admin Mode for Audit Logs: banner + cross-tenant endpoint (galactic/audit/logs)
- Audit page pagination (12 entries per page) with Previous/Next controls
- Platform Metrics: moved computation to backend PlatformMetricsService with 5-min cache
- New endpoint GET /api/galactic/metrics/platform replaces N+1 client-side fetches
- IME log tracker enhancements, DO telemetry pattern, and download progress improvements

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:40:37 +01:00
Oliver Kieselbach 80ee69276d feat: add Report Session to Autopilot Monitor Team (Private Preview)
Tenant Admins can report sessions for analysis via a new button on the
session details page. Creates a ZIP with session JSON, events JSON,
analysis results, timeline TXT, events CSV, optional agent log (5 MB
limit), and optional screenshot — uploaded to central blob storage.

Admin-config page gets a new Session Reports table (Galactic Admin only)
showing all submitted reports with comment, email, and blob reference.

Export utilities (generateUiExport, generateCsvExport) extracted to
shared lib/sessionExportUtils.ts for reuse across admin export and
report modal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 16:11:09 +01:00
Oliver Kieselbach 1f796bf6e3 feat: externalize rules to GitHub + API consolidation + preview mode
## Rules as GitHub single source of truth
- Add 75 individual JSON rule files (2 gather, 18 analyze, 55 IME patterns)
  under rules/gather/, rules/analyze/, rules/ime-log-patterns/
- Add JSON Schemas for all three rule types with VS Code IntelliSense support
- Add CI combine script (rules/scripts/combine.js) + GitHub Action that
  validates schemas with ajv-cli and generates rules/dist/ combined files
- Convert BuiltIn*.cs from hardcoded C# objects to embedded JSON resource loaders
- Add GitHubRuleRepository service to fetch dist/ files from raw.githubusercontent.com
- Add ReseedFromGitHubFunction: POST /api/rules/reseed-from-github?type=all|gather|analyze|ime

## IME Log Patterns
- Extend ImeLogPattern model with Description and IsBuiltIn properties
- Add ImeLogPatterns table + API constants
- Add TableStorageService CRUD methods for IME log patterns
- Add ImeLogPatternService (CRUD, merge, auto-seed from embedded JSON)
- Add ImeLogPatternsFunction: GET/PUT /api/rules/ime-log-patterns (Phase 1: read-only
  for tenants, Galactic Admin can enable/disable/edit globally)
- Add IME Log Patterns management page (/ime-log-patterns)
- Wire GetAgentConfigFunction to use ImeLogPatternService instead of static class

## API route consolidation
- All rule endpoints now under /api/rules/* for single certificate exclusion path:
  /api/rules/gather, /api/rules/analyze, /api/rules/ime-log-patterns,
  /api/rules/reseed-from-github

## Export buttons
- Add per-rule and export-all JSON download buttons to gather-rules,
  analyze-rules, and ime-log-patterns pages

## Admin configuration
- Replace old individual reseed buttons with unified "Fetch & Reseed from GitHub"
  (calls new GitHub endpoint) plus compact individual-type reseed buttons
- Add CDN cache hint: GitHub raw.githubusercontent.com caches up to 5 minutes

## Preview mode
- ?preview=1 URL param seeds localStorage.previewMode (persists across navigation)
- ?preview=0 clears it; toggle in Navbar settings dropdown (indigo, eye icon)
- IME Log Patterns nav link gated behind galacticAdminMode OR previewMode
- Preview badge shown on nav link when active for normal users

## Tests
- Add xUnit test project (net8.0) with 5 tests for embedded resource loading,
  known IME actions validation, and valid categories check

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 13:12:09 +01:00
Oliver Kieselbach 58b41a26b1 add temporary device blocking for rogue devices sending excessive data
Galactic Admins can block a device by serial number from the admin-configuration
page. The block is enforced at ingest time (after security validation, before
body decompression) and is configurable in duration (default 12h, max 30 days).
The agent receives a DeviceBlocked signal in the IngestEventsResponse and stops
all uploads for the session. Blocks persist across backend restarts via a new
BlockedDevices table in Azure Table Storage, with an in-memory cache for fast
ingest lookups.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 10:42:17 +01:00
Oliver Kieselbach ca39032ed4 Add Telegram notification for new Private Preview tenant signups
Sends a Telegram message to @AutopilotMonitor-PrivatePreview whenever
a new tenant signs up. The bot webhook URL is stored in the new
PreviewConfig table (PartitionKey=TelegramBot, RowKey=config,
WebhookUrl=<url>) — same lifecycle as PreviewWhitelist, easy to remove
after GA. Notification is fire-and-forget and non-fatal.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 23:13:22 +01:00
Oliver Kieselbach 31f4cddc13 Large rename job SerialNumberValidation to AutopilotDeviceValidation 2026-02-17 22:49:26 +01:00
Oliver Kieselbach 1f1b2cf2b4 usage of EntraId ClientId harmonised 2026-02-16 18:18:42 +01:00
Oliver Kieselbach 25cb01e315 rework agent cleanup
teams notifications on success or failure
2026-02-16 17:18:27 +01:00
Oliver Kieselbach f0465e76a1 added CORS logging and Agent max auth retry logic via config 2026-02-16 10:09:10 +01:00
Oliver Kieselbach 8227c909a5 added private preview support 2026-02-15 20:07:45 +01:00
Oliver Kieselbach 8858b588a2 SerialNumber Validaion implementation against Intune Auopilot 2026-02-13 22:14:02 +01:00