Commit Graph

62 Commits

Author SHA1 Message Date
Oliver Kieselbach f3b4d7571f feat(notifications): tenant-scoped bell notifications for hardware rejections
Until now, hardware-whitelist rejections were only visible to tenant admins
who actively navigated to the settings page or had a webhook configured.
Admins missed new rejected models if neither was the case.

This adds a parallel tenant-scoped notification pipeline (separate from the
existing GlobalAdmin-only feed): on the first-ever HardwareNotAllowed
distress per (tenantId, manufacturer-lower, model-lower), a persistent
bell notification is created with a deep link to the hardware-whitelist
page. Lifetime dedup via a dedicated tracker table makes it a one-shot per
model. The webhook path is unchanged and fires in parallel.

Routes are gated TenantAdminOrGA + TenantScoping.Jwt for now; an Operator
follow-up (new TenantAdminOrOperator policy) is tracked separately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 20:07:06 +02:00
Oliver Kieselbach e55ad3e22d feat(analyze): retry-safe queue for auto-analyze, drop fire-and-forget
The previous design ran RuleEngine in Task.Run after HTTP 200 — Functions scale-in
could kill it mid-flight, leaving rule results un-persisted and forcing manual
"Analyze Now" clicks. New design enqueues onto analyze-on-enrollment-end and
runs the engine in a BackgroundService worker with visibility-timeout retry +
poison move after 5 attempts (mirrors telemetry-index-reconcile pattern).

Three trigger sites cut over: V1 ingest, V2 EventIngestProcessor primary, V2
post-vulnerability re-run. Manual "Analyze Now" remains as fallback.

Plus retry-semantics fix: RuleEngine.AnalyzeSessionAsync no longer swallows
storage exceptions in an outer try/catch (per-rule inner catch stays), and the
handler now throws when StoreRuleResultAsync returns false. Without this the
worker would have deleted messages on transient Table Storage failures and never
retried.

Tests: +4 failure-propagation, +6 envelope round-trip, 762 backend pass.

Follow-ups noted: TableStorageService helpers still swallow exceptions one
layer below; SignalRNotificationService needs interface for handler-level
unit tests; V1 ingest path missing vulnerability_report synth event.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 19:27:28 +02:00
Oliver Kieselbach 5b9410eb9c feat(vuln): persist MSRC + KEV reference caches as blob snapshots
Container App scale-to-zero clears the in-memory MSRC and KEV indexes on
every cold start, leaving a window where session correlation runs without
those data sources until the next refresh. Re-fetching ~1800 MSRC CVEs
plus the KEV catalog from upstream on every cold start is wasteful and
slow; persisting the snapshots to blob solves both problems.

New BlobBackedReferenceCache<T> generic helper handles single-blob JSON
snapshots in the autopilotmonitor / vulnerability-data container:
- msrc-index.json — full MSRC CVRF index (~350 KB for last 6 Patch Tuesdays)
- kev-catalog.json — full CISA KEV catalog (~500 KB)

Both services gained:
- EnsureHydratedAsync — idempotent, semaphore-guarded blob load
- Blob-write at the end of RefreshAsync (manual trigger + daily timer)
- Hydrate guard set after refresh so the no-op path is taken next time

VulnerabilityCacheWarmer (IHostedService) kicks off both EnsureHydratedAsync
calls fire-and-forget at app startup so re-deploys do not pay a blob-read
latency cost in their cold-start window. The correlation hot path also
calls EnsureHydratedAsync as a defensive backstop — idempotent and
semaphore-protected, so a request arriving mid-warmup awaits the same
in-flight load instead of duplicating it.

NVD's per-CPE cache stays in Table Storage — random-access lookup by CPE
key plus per-key 7d stale-refresh logic is the right shape for that
workload and would degrade if forced into a single-blob model.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 16:46:54 +02:00
Oliver Kieselbach be63dad534 feat(backend): wire MSRC into correlation + sampling-bypass observability
MsrcApiClient was injected into VulnerabilityCorrelationService for months
but never invoked — every report shipped with source="NVD" only. New
MsrcCorrelationService owns refresh + per-CVE lookup against an in-memory
index built from the last 6 Patch Tuesdays; daily VulnerabilityDataSync
populates it; CorrelateAsync enriches each finding with msrcDocumentId,
msrcReleaseDate, msrcSeverity, msrcBaseScore, msrcVector and flips source
to "NVD+MSRC" when the CVE was published in the indexed window.

Health check also surfaced that all worker-side traces of the daily sync
and CPE auto-resolve were sampled out of App Insights despite running.
Adds Event to host.json excludedTypes and emits TelemetryClient events
for VulnerabilityDataSyncCompleted, VulnerabilityCorrelationCompleted,
CpeAutoResolveHit/Miss, CpeMappingPersisted so customEvents queries can
prove freshness without depending on sampled traces.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 13:16:22 +02:00
Oliver Kieselbach b28b5536ff refactor(backend): replace IndexReconcile QueueTrigger with BackgroundService worker
The QueueTrigger binding required a Functions-host-specific app setting
(`<Connection>__queueServiceUri` with `__credential=managedidentity`) that
diverged from the project's storage-access pattern. Tables, blobs and the
index-reconcile producer all use a single `AzureStorageAccountName` (Managed
Identity) or `AzureTableStorageConnectionString` (full conn-string) and build
their clients with `DefaultAzureCredential` directly via DI — no host-side
binding glue.

In production the trigger silently failed to bind on every Function-App
worker start ("Error indexing method 'Functions.IndexReconcile'. Storage
account connection string 'AzureWebJobsAzureTableStorageConnectionString'
does not exist"), so messages enqueued by the producer would have piled up
unconsumed once `EnableIndexDualWrite` was flipped on.

Replaces:
- IndexReconcileFunction.cs (40 LoC, QueueTrigger) → DELETED
- New IndexReconcileQueueWorker.cs (BackgroundService) — same DI-based
  config resolution as AzureQueueIndexReconcileProducer, full
  QueueTrigger-platform parity (visibility-timeout retries, poison-queue
  move after 5 failed attempts, malformed-JSON drop, idle 10s sleep).

Also pre-existing TableInitializerService is registered as IHostedService
in the same project, so the pattern is established.

Build: 0 warnings, 0 errors. Backend tests: 736/736 passed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 15:06:25 +02:00
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