Commit Graph

307 Commits

Author SHA1 Message Date
Oliver Kieselbach 119af503af replace app_tracking_summary apps array with state-breakdown counters and add InstallProgress tracker
Fixes Azure Table Storage 64KB truncation by removing the full apps array
from app_tracking_summary (could be 150+ apps) and replacing it with
lightweight state-breakdown counters (downloading, installing, installed,
skipped, failed, pending). Summary emission is now event-driven with
30s timer as backstop. New InstallProgress UI component shows per-app
install state with duration tracking. ConfigVersion 10 -> 11.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 17:56:56 +01:00
Oliver Kieselbach 17b3c4cdac fine tuned DownloadTracker to recognize Skipped apps and better progressbar behavior on finisched (100 percent) downloads 2026-03-02 17:47:09 +01:00
Oliver Kieselbach 0c739e96ca HelloWaitTimeoutSeconds is now configurable in Configuration 2026-03-02 16:35:58 +01:00
Oliver Kieselbach 53bea70adb gracefully display truncated event data in UI
Backend: DeserializeEventData now returns raw string in _rawDataJson key
when JSON parsing fails (truncated data), instead of empty dict.
UI: EventTimeline detects _rawDataJson and shows an amber warning with
the raw text, so truncated events remain inspectable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 16:15:00 +01:00
Oliver Kieselbach 3371a11641 truncate event properties to prevent Azure Table Storage 64KB limit errors
DataJson and Message are now capped at 30K chars before writing to Table Storage,
preventing PropertyValueTooLarge failures that silently dropped events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 15:43:27 +01:00
Oliver Kieselbach 1f835c20ac added UserDriven vs WhiteGlove stats 2026-03-02 15:17:38 +01:00
Oliver Kieselbach 57e4b512f6 fixed whiteglove lifcycle flow storage table states and status 2026-03-02 15:05:51 +01:00
Oliver Kieselbach 4a3caaaa86 add WhiteGlove Part 2 signal for clean Pending→InProgress transition
Agent persists a whiteglove.complete marker when Part 1 finishes. On Part 2
boot, the agent detects the marker, emits a whiteglove_resumed event, and
clears it. The backend handles whiteglove_resumed by transitioning the session
from Pending to InProgress — only if actually Pending (guards against
overwriting Succeeded/Failed). Registration also retries with exponential
backoff (5 attempts) to handle delayed network during OOBE boot.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 14:50:25 +01:00
Oliver Kieselbach 2de8022c7b agent resilliency 2026-03-02 13:50:46 +01:00
Oliver Kieselbach 5d058bf84c fix DateTime Kind=Local causing NotSupportedException on Azure Table Storage writes
Windows EventLog record.TimeCreated returns DateTime with Kind=Local. The Azure
Data Tables SDK rejects Local DateTimes with NotSupportedException, causing
UpdateSessionStatusAsync to fail deterministically for every WhiteGlove session
(Shell-Core EventID 62407 = whiteglove_complete). This was the root cause of the
systematic 6/6 WhiteGlove failures, not ETag conflicts or transient Azure errors.

Agent fix: convert record.TimeCreated to UTC at all 3 EventLog timestamp sources.
Backend fix: EnsureUtc() safety net on all 11 DateTime writes to the sessions table.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 13:20:11 +01:00
Oliver Kieselbach 8a8002491d fix WhiteGlove fallback to also set Status=Pending and retry transient errors
The initial fix (43c2674) resolved the 500 loop but the fallback only set
IsPreProvisioned, leaving Status=InProgress instead of Pending. This extends
the fallback to set both fields and adds retry logic for transient Azure
errors (429/503/408) in UpdateSessionStatusAsync.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 12:33:53 +01:00
Oliver Kieselbach 7e5a7b622d fixed wrong PhaseName set for multipe events from the agent -> UI timeline confusion otherwise 2026-03-02 12:20:13 +01:00
Oliver Kieselbach e63e48da1a added agent emergency reporter 2026-03-02 11:51:25 +01:00
Oliver Kieselbach 43c2674a17 fix WhiteGlove spool drain 500 loop that blocks event upload and causes data loss
The IngestEventsFunction verification check returned HTTP 500 when the
WhiteGlove IsPreProvisioned session update failed, causing the agent to
retry the same batch in a tight loop (same whiteglove_complete event,
same failing update, same 500). After 20 retries the agent gave up with
events stuck in the local spool and the session metadata broken.

- Add unconditional fallback (ETag.All merge) via SetSessionPreProvisionedAsync
  when UpdateSessionStatusAsync fails despite internal ETag retries
- Replace the 500 verification response with a warning log + continue
  with 200 so the agent can drain its spool
- Simplify SetSessionPreProvisionedAsync to use ETag.All unconditional
  merge, removing the unnecessary read-then-write pattern

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 10:55:12 +01:00
Oliver Kieselbach f1961f0b9e changed default info log message for better tracability during default logging bahaviour 2026-03-02 10:18:42 +01:00
Oliver Kieselbach a991b9f94f more accurate session generatUi export function 2026-03-02 09:50:07 +01:00
Oliver Kieselbach 2087ac33f7 agent performance optimized 2026-03-01 20:30:33 +01:00
Oliver Kieselbach 93fcc71162 changed default for localadminanalyzer definition 2026-03-01 20:10:53 +01:00
Oliver Kieselbach 10250161ab added agent Analyzer system incl. first analyzer - LocalAdminAnalyzer
Trigger Options: AtStartup, AtShutdown
2026-03-01 19:41:39 +01:00
Oliver Kieselbach e1c6617b53 feat: replace broken JSON blobs in session report ZIP with proper CSV exports
- Remove session.json/events.json/analysis-results.json (serialized as
  {"ValueKind":1} due to System.Text.Json.JsonElement + Newtonsoft mismatch)
- Add session.csv, events.csv, ruleresults.csv — each mirroring the exact
  Azure Table Storage schema (PartitionKey, RowKey, all columns)
- Add timeline.txt (UI timeline view) is retained
- SessionSummary API response now includes OsBuild, OsEdition, OsLanguage,
  IsUserDriven, AgentVersion so session.csv is fully populated
- events.csv: Severity as "Name (int)", PhaseName derived column appended
- ruleresults.csv: JSON columns (RemediationJson etc.) kept as JSON strings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 18:13:17 +01:00
Oliver Kieselbach 6a5b059ed3 added Report Session notification channel 2026-03-01 17:42:37 +01:00
Oliver Kieselbach 14a3ce5b9c feat: fix session report download + add admin note feature
- Fix 500 error on download URL: replace GenerateSasUri() with
  blobClient.Uri (SAS already embedded in service-level connection string)
- Add AdminNote field to SessionReportMetadata shared model
- Read AdminNote from Table Storage in GetAllReportsAsync
- Add UpdateAdminNoteAsync to SessionReportService
- New PATCH /api/galactic/session-reports/{reportId}/note endpoint
- Frontend: editable Admin Note textarea in report detail modal with
  inline save feedback; Admin Note column in reports table with icon
- Add copy-to-clipboard buttons for Session ID, Tenant ID,
  Submitted By, and Email fields in report detail modal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 17:37:52 +01:00
Oliver Kieselbach 3f7261f200 feat: show inline success/error feedback in ReportSessionModal
After submitting a report, the modal now displays a green success block
(with checkmark) or a red error block (with message) directly inside the
dialog instead of only relying on the auto-dismissing notification banner.
On success the form is hidden and the button becomes a green Close button;
on error the form stays visible for retry. The modal resets cleanly on
re-open. Report Session button is now always visible (not gated on
adminMode).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 17:16:15 +01:00
Oliver Kieselbach 01c76b3ce7 feat: add session report download — backend SAS URL endpoint + frontend Download ZIP button
- New Azure Function GET /api/galactic/session-reports/download-url generates a 15-minute SAS URL for a session report blob (Galactic Admin only)
- SessionReportsSection modal now shows a Download ZIP button that fetches the SAS URL and triggers a browser download

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-01 17:14:24 +01:00
Oliver Kieselbach 76f56c3b56 show Report Session to Tenant Admins on SessionDetailPage 2026-03-01 16:59:02 +01:00
Oliver Kieselbach 0d2f2ae38c refactor: rename /api/preview-whitelist to /api/preview/whitelist
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 16:50:18 +01:00
Oliver Kieselbach a2de05fe33 refactor: reorganize metrics/stats API routes and fix session-reports 404 handling
- Rename app-metrics → metrics/app (tenant + galactic)
- Rename usage-metrics → metrics/usage (tenant)
- Rename platform/usage-metrics → galactic/metrics/usage
- Rename platform-stats → stats/platform (anon whitelist updated)
- SessionReportService: gracefully handle missing table (404) with empty list
- SessionReportsSection: treat 404 as no reports yet (suppress error)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 16:47:55 +01:00
Oliver Kieselbach f092d601db refactor: move session-reports endpoint to /api/galactic/session-reports
Consistent with /api/galactic/sessions and /api/galactic/app-metrics —
all cross-tenant Galactic Admin endpoints live under /api/galactic/.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 16:26:59 +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 d619fd87ba changelog update 2026-03-01 16:06:01 +01:00
Oliver Kieselbach 4017d587a6 changed order on admin-config site 2026-03-01 15:50:27 +01:00
Oliver Kieselbach bfb185b8f1 fix timeline for whiteglove enrollment in UI 2026-03-01 15:41:13 +01:00
Oliver Kieselbach 6083a64ec0 layout re-order in admin-config + session table storage fix 2026-03-01 15:02:17 +01:00
Oliver Kieselbach be317a91dc fix: simplify session status logic — only key events change Status
WhiteGlove sessions showed IsPreProvisioned=false and Status=InProgress
despite whiteglove_complete being stored in Events table. Root cause:
phase_changed events repeatedly wrote Status=InProgress via
UpdateSessionStatusAsync, creating ETag contention and silent failures.

Changes:
- Remove InProgress writes from IngestEventsFunction — Status is only
  set by key events (enrollment_complete, enrollment_failed,
  whiteglove_complete, gather_rules_collection_completed)
- Phase-change events now use IncrementSessionEventCountAsync with
  new currentPhase parameter (lightweight, no Status write)
- Add verification after whiteglove_complete: return HTTP 500 if
  IsPreProvisioned not persisted, so agent retries the batch
- Add IsPreProvisioned to SignalR sessionDelta for real-time UI updates
- Remove now-dead promotion rule from UpdateSessionStatusAsync

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 14:42:40 +01:00
Oliver Kieselbach d012c26360 fixes for whiteglove enrollment on agent side and backend. status pending was never set correctly
fix platform-metrics missing agent version and UI filter for it
2026-03-01 12:13:24 +01:00
Oliver Kieselbach 80f5e3790f fix: compute DurationSeconds from Events table at completion to avoid race condition
The performance commit (477b573) removed GetEarliestSessionEventTimestampAsync
from UpdateSessionStatusAsync, relying solely on the current batch's
earliestEventTimestamp. When concurrent Azure Function invocations process
different batches for the same session, the completion batch could read a stale
StartedAt before the earlier batch's Merge committed — producing wrong
DurationSeconds (e.g. 3s instead of 679s).

Fix: re-introduce the single-row Events table lookup (maxPerPage: 1) only at
completion (Succeeded/Failed) — once per session lifecycle, minimal cost.
Revert MapToSessionSummary to use stored DurationSeconds with null-fallback
for in-progress sessions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 10:16:11 +01:00
Oliver Kieselbach 2092a1f00c fix duraton in session list 2026-03-01 09:18:08 +01:00
Oliver Kieselbach 56e83e5281 fix layout 2026-03-01 01:20:00 +01:00
Oliver Kieselbach c6227f546f feat: add agent self-metrics collector and platform metrics page
Add AgentSelfMetricsCollector that measures the agent's own CPU, memory,
threads, handles, and HTTP network traffic (bytes up/down, latency,
request count) with zero overhead — no WMI, no PerformanceCounters,
just Process properties and lock-free Interlocked counters.

NetworkMetrics tracks all BackendApiClient HTTP calls via Stopwatch and
byte counting in finally blocks. The collector emits agent_metrics_snapshot
events (Severity.Debug) through the existing spool/upload pipeline.

Add Galactic Admin-only /platform-metrics page that aggregates agent
footprint data across sessions with CPU/memory/network assessment badges,
per-session breakdowns, and agent version distribution.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 01:11:20 +01:00
Oliver Kieselbach 477b573f1f perf: eliminate redundant table scans, improve concurrency, optimize SignalR
- Remove full event-count recount (partition scan) from UpdateSessionStatusAsync;
  IncrementSessionEventCountAsync already maintains the count atomically
- Remove redundant GetEarliestSessionEventTimestampAsync calls from
  UpdateSessionStatusAsync and IncrementSessionEventCountAsync; the caller
  (IngestEventsFunction) already computes and passes the timestamp
- Switch UpdateSessionStatusAsync from Replace to Merge mode, writing only
  changed fields to drastically reduce ETag conflicts under concurrency
- Increase ETag retry limit from 3 to 5 with jitter-based exponential backoff
- Add server-side time-range filter (90 days) to UsageMetrics queries,
  avoiding loading the entire sessions table into memory
- Remove full session object from SignalR eventStream message; session delta
  updates now flow via the existing newevents tenant-group message
- Add newevents handler to session detail page for live session delta updates
- Auto-rejoin SignalR groups on reconnect to prevent silent update loss

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 23:49:47 +01:00
Oliver Kieselbach 65f4c5a2bb more efficient WMI queries 2026-02-28 23:48:09 +01:00
Oliver Kieselbach ab08d48d94 added missing event timestamps 2026-02-28 21:23:25 +01:00
Oliver Kieselbach 4bf2b965ae fix: compute SAS URL expiry on input change for instant feedback
Move SAS expiry calculation from page-level state into DiagnosticsSection
so the expiry indicator updates immediately when the user types or pastes
a new URL, instead of requiring a save and page reload.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 20:47:52 +01:00
Oliver Kieselbach 4e21ae3008 security: enforce admin authorization, fix error leakage, prevent injection
- H1: Require TenantAdmin or GalacticAdmin for mutative tenant endpoints
  (DeleteSession, MarkSessionFailed, UpdateTenantConfiguration,
  AnalyzeRules CRUD, GatherRules CRUD)
- M1: Add EnsureValidGuid validation to TenantOffboardFunction to
  prevent OData injection via tenantId route parameter
- M2: Remove ex.Message from error responses in UsageMetrics,
  PlatformUsageMetrics, TriggerMaintenance, TenantOffboard,
  ReseedFromGitHub to prevent internal error leakage
- M4: Use requireGalacticAdmin on ProtectedRoute and server-derived
  user.isGalacticAdmin instead of localStorage for admin-configuration
- N1: Add 1-second Regex timeout to agent GatherRuleExecutor and
  ImeLogTracker to prevent ReDoS from malicious patterns

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 20:46:50 +01:00
Oliver Kieselbach 59d7988092 changed wording 2026-02-28 19:33:36 +01:00
Oliver Kieselbach 13a781bd34 refactor: reorganize project structure 2026-02-28 19:25:34 +01:00
Oliver Kieselbach 4ce531191b feat: RuleStates table + community rules + remove global edit/delete
- New RuleStates table stores per-tenant enabled/disabled state separately
  from rule definitions, so built-in/community rule updates propagate
  centrally without losing tenant preferences
- New IsCommunity flag on GatherRule and AnalyzeRule models; community
  rules behave like built-in but show a Community badge in the UI
- Merge logic updated: global partition holds definitions (built-in +
  community), RuleStates holds per-tenant toggles, tenant partition
  holds custom rules only
- Fixed AnalyzeRule Trigger field not being persisted to/from Table Storage
- Fixed EnsureBuiltInRulesSeededAsync comparison to include Version,
  Trigger, and TriggerPhase so definition changes auto-propagate
- Removed Edit (Global) and Delete (Global) from Gather/Analyze rules UI;
  rule management is now exclusively via Git + Reseed
- Added Community filter option to type filter dropdowns
- ReseedFromGitHubFunction now deletes both IsBuiltIn and IsCommunity
  rules from global partition before reseeding
- GalacticAdminService dependency removed from Gather/AnalyzeRulesFunction

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 17:33:46 +01:00
Oliver Kieselbach 35f7f59520 fix SessionDetailsPage flickering 2026-02-28 16:48:25 +01:00
Oliver Kieselbach 46d947563b Reseed ime pattern button + tenant management on desktop layout 2026-02-28 13:48:16 +01:00
Oliver Kieselbach 17e8e67f9b fix navbar 2026-02-28 13:27:16 +01:00