Commit Graph

289 Commits

Author SHA1 Message Date
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 19856ad331 Merge branch 'main' of https://github.com/okieselbach/Autopilot-Monitor 2026-02-28 16:48:29 +01:00
Oliver Kieselbach 35f7f59520 fix SessionDetailsPage flickering 2026-02-28 16:48:25 +01:00
okieselbach 0b322f2d2c chore: regenerate rules/dist from individual files 2026-02-28 13:12:14 +00:00
Oliver Kieselbach 65860d7e9b version bump 2026-02-28 14:11:35 +01:00
Oliver Kieselbach e5b0faf692 fixed combine workflow to allwow push 2026-02-28 14:10:39 +01:00
Oliver Kieselbach a1c9e9a576 chagned gather rule phase definition 2026-02-28 14:07:57 +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
Oliver Kieselbach 64a9a0aa99 chore: unignore rules/dist/ and add initial combined rule files
rules/dist/ was excluded by the global dist/ .gitignore pattern.
Added !rules/dist/ exception so the combine-rules workflow can
commit the generated files. Also adds the initial dist files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 13:24:15 +01:00
Oliver Kieselbach 7cb5cf040c fix: add count_per_group_gte operator to analyze rule schema
Adds the missing operator to the enum so ANALYZE-APP-006.json passes
schema validation in the combine-rules workflow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 13:19:35 +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 923f03ea39 fix fleet health 2026-02-28 00:22:06 +01:00
Oliver Kieselbach 5f848aa7b5 fix: gather rules toggle bug, global delete, mobile layout improvements
- fix: handleToggleRule sent only {enabled} — now sends full rule object
  to prevent overwriting all fields with empty values on toggle
- feat: GatherRule global delete (Galactic Admin) — new DeleteGlobalRuleAsync,
  ?global=true query param on DELETE endpoint, Delete (Global) button in UI
- fix: admin-configuration page mobile layout — Maintenance, Reseed Analyze,
  Reseed Gather panels: action buttons moved to bottom (flex vertical)
- fix: tenant management list mobile layout — badges and buttons below info text
- fix: session detail event timeline — filter badges + expand/collapse in one
  flex-wrap row; expand/collapse show icon-only on mobile, text on sm+

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 00:12:40 +01:00
Oliver Kieselbach b0ff5f08c8 fix layout 2026-02-27 22:09:11 +01:00
Oliver Kieselbach 54a3327c2c fix: fleet health model bar chart - full name visible, slimmer bar
Model name now renders fully above the bar (no truncation, break-words).
Bar height reduced from h-4 to h-2. Percentage and device count shown
inline right-aligned next to the name row.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 21:58:00 +01:00
Oliver Kieselbach cb7b270144 fix wording 2026-02-27 21:51:17 +01:00
Oliver Kieselbach 084e02d31e changelog update 2026-02-27 21:48:06 +01:00
Oliver Kieselbach 56c51cd888 feat: shorten tenant ID in galactic session table + copy on click
Tenant ID column in galactic admin mode now shows only the first two
GUID segments (e.g. b54dc1af-5320). Full ID shown as tooltip. Click
copies the full tenant ID to clipboard; copy icon appears on hover.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 21:46:29 +01:00
Oliver Kieselbach fafff58c92 fix: diag log path entries use full-width layout with line-wrap, remove core collector hint
- Path and description stack vertically in their own row (break-all for long paths)
- Replaced rounded-full chips with rounded-lg full-width entries in both settings and admin-config
- Removed 'Core collectors Hello Detector...' info box from settings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 21:33:31 +01:00
Oliver Kieselbach 3d7c4a0af2 feat: configurable diagnostics log paths + reseed gather rules
- Add DiagnosticsLogPath model and DiagnosticsPathGuards (agent-side allowlist)
- Global paths configurable by Galactic Admin (AdminConfiguration.DiagnosticsGlobalLogPathsJson)
- Tenant-specific paths configurable in Settings (TenantConfiguration.DiagnosticsLogPathsJson)
- Backend merges global + tenant paths and delivers via AgentConfigResponse (ConfigVersion 9)
- Agent reads merged paths in DiagnosticsPackageService, validates via guards before adding to ZIP
- Admin Configuration page: new Diagnostics Log Paths panel (teal/cyan)
- Settings page: Additional Log Paths section with global read-only chips + tenant chip editor
- Add reseed endpoint and service method for gather rules (POST /api/gather-rules/reseed)
- Docs: SectionGatherRules guard lists collapse by default; SectionSettings adds Additional Log Paths row

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 21:21:30 +01:00
Oliver Kieselbach a99a1c02af fix layout 2026-02-27 20:21:16 +01:00
Oliver Kieselbach 6f6ecb311c Refactorings 2026-02-27 20:06:14 +01:00
Oliver Kieselbach f07a354c35 fix: 8-second timeout on /api/auth/me to prevent cold-start hang on landing page
The fetchUserInfo call to GET /api/auth/me had no timeout. When Azure
Functions are cold (scale-to-zero), this could block for 20-60 seconds,
holding isLoading=true and showing only the spinner on the landing page.

Add an AbortController with an 8-second deadline. On timeout the abort
signal causes the fetch to throw, which is caught by the existing catch
block that falls back to MSAL token claims — the user can still log in
normally. isGalacticAdmin/isTenantAdmin will be false until the backend
is warm and the next auth refresh succeeds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 19:30:49 +01:00
Oliver Kieselbach b0c66bdf70 feat: gather rules JSON toggle, parameter UI, built-in rules, analyze rules docs
- Add Form/JSON toggle to Gather Rules editor (create + edit), matching Analyze Rules pattern
- Add missing parameter UI for eventlog (source/provider), file (readContent), and logparser (pattern, trackPosition, maxLines) collector types
- Add two disabled built-in gather rules: dsregcmd /status on DevicePreparation phase change, and last startup event (Event ID 12) from System eventlog
- Fix file collector to read last 4000 chars instead of first (more useful for log files)
- Docs: expand registry allowlist to all 21 prefixes, update file read description
- Docs: add comprehensive Analyze Rules section with confidence scoring, condition types, trigger types, and examples
- Add Documentation link in Analyze Rules page header

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 19:25:21 +01:00
Oliver Kieselbach 8eb8c31e90 feat: EventLog collector rewrite, dynamic gather rule UI hints, docs
Agent:
- Rewrite EventLog collector from System.Diagnostics.EventLog to
  EventLogReader — supports operational/analytic logs (e.g.,
  Microsoft-Windows-Shell-Core/Operational), not just classic logs
- New parameters: eventId (XPath filter), messageFilter (*contains*),
  source (provider name), maxEntries (1-50)
- Event entries now include Event ID in output: [ID:62407]

UI (gather-rules):
- Dynamic target placeholder and hint text per collector type
- EventLog parameter fields: Event ID, Max Entries, Message Filter
- buildParameters() serializes params per collector type (create+edit)
- on_event placeholder updated to show enrollment_complete/failed
- Documentation link in header → /docs#gather-rules

Docs:
- New "Gather Rules" section with all 6 collector types documented
- Each collector: target format, parameters, allowlists, examples
- Trigger types documented (startup, interval, phase_change, on_event)
- Hash-based deep linking from gather-rules page

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 18:58:22 +01:00
Oliver Kieselbach fc963ef85e fix: show domainName (tenantId) in Device Block Management dropdowns
Consistent with Session Event Export tenant dropdown format.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 17:48:04 +01:00
Oliver Kieselbach a553f09ec3 fix: CSV export row order and RowKey, tenant dropdown, input cleaning
- CSV now sorted by Timestamp+Sequence (matches Azure Table order exactly)
- Added RowKey as first CSV column (format: yyyyMMddHHmmssfff_Sequence)
- Backend: map entity.RowKey in MapToEnrollmentEvent, expose via EnrollmentEvent
- Tenant ID field replaced with dropdown (domainName + tenantId) + free-text input
- cleanId() helper strips leading/trailing quotes and whitespace from ID inputs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 17:41:24 +01:00
Oliver Kieselbach 17cfc91273 fix: persistent sequence counter across boots, spool RemoveEvents collision fix
The event sequence counter reset to 0 on every agent restart, causing three
bugs in WhiteGlove (pre-provisioning) sessions: spool sort order mixed boot
events, backend status regressed from Pending to InProgress, and UI timeline
split broke. Additionally, EventSpool.RemoveEvents used a glob pattern that
could delete files from both boots sharing the same sequence number.

Changes:
- SessionPersistence: persist sequence in session.seq alongside session.id
- EventSpool: RemoveEvents uses exact filename instead of glob, new
  GetMaxSequence for crash recovery
- MonitoringService: init sequence from persistence + spool ceiling, periodic
  save every 50 events + on critical events, save before WhiteGlove exit
- TableStorageService: document WhiteGlove guard as promotion rule
- EnrollmentEvent: clarify Timestamp is agent-side, not server-side

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 17:39:53 +01:00
Oliver Kieselbach 6d7548d49f fix hello_wait_timeout falsely triggering enrollment_complete when Hello policy is enabled
Root cause found via WhiteGlove session log (session-46ec9146):
On the user's second boot the esp_exiting event triggered the 30s Hello-Wait
timer. The Hello wizard had not yet appeared within those 30s, so
OnHelloWaitTimeout() emitted hello_wait_timeout and immediately fired
HelloCompleted → enrollment_complete. This was wrong: Hello policy was
explicitly detected as enabled (hello_policy_detected: helloEnabled=true).
The Hello wizard launched shortly after (hello_provisioning_willlaunch at seq
27), but the agent had already sent enrollment_complete.

Fix: track _isHelloPolicyEnabled in CheckHelloPolicy(). In OnHelloWaitTimeout,
if Hello policy is known to be enabled, do not fire HelloCompleted — instead
emit the hello_wait_timeout event with action=extended_wait and start the long
HelloCompletion timer (1500s). This gives the wizard up to 25 minutes to appear.
The enrollment_complete path via timeout is now only taken when Hello policy is
disabled or not yet detected.

The original session (46ec9146) was also affected by the spool-drain bug
(whiteglove_complete was seq 126+, never uploaded before exit), so the session
never reached Pending. Both bugs contributed to the incorrect Succeeded result.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 16:18:14 +01:00
Oliver Kieselbach 2e000d4b78 improve WhiteGlove shutdown: proper drain, clean stop, preprov diagnostics
- Replace ad-hoc Task.Run block with dedicated HandleWhiteGloveComplete()
  method mirroring HandleEnrollmentComplete() quality:
  - StopEventCollectors() + _spool.StopWatching() before draining
  - Full spool drain loop (same fix as previous commit, now in proper place)
  - Task.Delay(2000) for final network transmission
  - Diagnostics package upload with "preprov" suffix before exit
  - Structured logging matching the enrollment complete sequence
- DiagnosticsPackageService.CreateAndUploadAsync() gains optional
  fileNameSuffix parameter: "preprov" yields
  AgentDiagnostics-{sessionId}-{ts}-preprov.zip, distinguishable
  from the later full-enrollment package uploaded after Part 2
- UploadDiagnosticsWithTimelineEvents() passes suffix through to service

WhiteGlove devices now have a preprov diagnostics package in Pending
state, and a full package after user enrollment completes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 16:03:04 +01:00
Oliver Kieselbach 2a969bf1e1 fix WhiteGlove final upload only draining first batch
UploadEventsAsync fetches at most MaxBatchSize (100) events per call.
If whiteglove_complete was beyond event #100 in the spool (e.g. Seq 126),
it was never sent before Environment.Exit(0) — leaving the session stuck
on InProgress instead of transitioning to Pending.

Fix: loop until spool is empty (with a safety cap of 20 iterations) so
all events including whiteglove_complete are guaranteed to reach the backend.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 15:52:19 +01:00
Oliver Kieselbach 865410fc21 fix: remove timer-based redirect delay in ProtectedRoute
Replace the fragile setTimeout(500ms) with a deterministic guard:
wasAuthenticated.current already prevents spurious redirects during
MSAL's double handleRedirectPromise cycle. The real fix was setting
navigateToLoginRequestUrl: false in msalConfig. No timer needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 15:42:49 +01:00
Oliver Kieselbach 97ca48f56a fix: stop MSAL-triggered navigation to dashboard while on session detail page
Root cause: MSAL fires handleRedirectPromise twice on page load, causing
isAuthenticated to briefly flip false between the two settlements. This
triggered ProtectedRoute to redirect to '/', which then re-authenticated
and redirected to '/dashboard', causing the dashboard page to mount and
unmount periodically while the user was viewing a session.

Two fixes:
1. navigateToLoginRequestUrl: false — prevents MSAL from navigating to
   the stored login request URL after redirect settlement, eliminating
   the spurious navigation that caused the dashboard to mount.
2. ProtectedRoute unauthenticated redirect now waits 500ms before
   executing, giving MSAL time to complete its double-settlement cycle
   before we conclude the user is truly unauthenticated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 15:40:32 +01:00
Oliver Kieselbach 24862ca1de fix: allow retry when fetchSessionDetails fails due to token not ready
When galacticAdminMode is true, the initial data fetch effect fires
immediately (before MSAL settles). If getAccessToken() returns null,
hasInitialFetch was left as true, permanently blocking any retry.
The session page would load empty and never recover.

Fix: reset hasInitialFetch on token/network failures so the effect
can retry. Added `user` to dependencies so the effect re-fires once
MSAL settles and user info is available.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 15:29:24 +01:00
Oliver Kieselbach a5e83f066b download progress sanity check if miscalculation occurs 2026-02-27 15:12:11 +01:00
Oliver Kieselbach 1368678f48 fix: prevent ProtectedRoute from flashing children on transient MSAL auth-state changes
When MSAL's handleRedirectPromise re-settles or token renewal changes
inProgress, isAuthenticated/isLoading can briefly flip, causing
ProtectedRoute to unmount all children (showing loading spinner or null).
This made the session detail page go blank for 1-3s periodically.

Fix: track wasAuthenticated via useRef. Once authenticated, keep children
mounted through transient auth-state flips. Loading spinner and redirect
to landing only apply on the very first load (never been authenticated).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 15:11:17 +01:00
Oliver Kieselbach 0f6a0cc21d added private preview changelog entry for first try pre-provisioning support 2026-02-27 14:40:46 +01:00
Oliver Kieselbach b78b872f68 implement WhiteGlove (Pre-Provisioning) support end-to-end
- Rename HelloDetector → EspAndHelloTracker (class tracks both ESP and Hello)
- Detect CommercialOOBE_ESPProgress_WhiteGlove_Success (EventID 62407) and emit
  whiteglove_complete event; WhiteGlove check placed before generic OOBE_ESP.*Exiting
  regex to avoid false match; idempotency guard prevents duplicate events
- Detect CommercialOOBE_ESPProgress_Failure (EventID 62407) and emit esp_failure
  event with Error severity for timeline visibility
- Agent exits gracefully after WhiteGlove (Environment.Exit, no DeleteSessionId,
  no enrollment-complete marker); scheduled task restarts on next boot
- EnrollmentTracker subscribes to WhiteGloveCompleted event, stops summary timer,
  emits whiteglove_complete without writing completion marker or deleting IME state
- Backend: whiteglove_complete transitions session to SessionStatus.Pending;
  SetSessionPreProvisionedAsync marks session for split-timeline rendering
- Backend: Pending → InProgress auto-resume on agent re-registration (WhiteGlove Part 2)
- Backend: GetExcessiveDataSendersAsync excludes IsPreProvisioned sessions to prevent
  false blocking of devices resuming after weeks in storage
- Shared: SessionStatus.Pending added; IsPreProvisioned added to SessionSummary;
  WhiteGloveComplete event type constant added
- UI: Pending status badge (amber) in dashboard and session detail
- UI: Split timeline for WhiteGlove sessions — Pre-Provisioning Part + User Enrollment
  Part; Awaiting User Enrollment info box when Part 2 has no events yet
- UI: Mark as Failed button enabled for Pending sessions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 14:36:24 +01:00
Oliver Kieselbach 03ba8ab905 added WhiteGlove example event as comment for later WhiteGlove detection implementation 2026-02-26 23:53:45 +01:00
Oliver Kieselbach b40eee2c59 update architecture diagram with mTLS 2026-02-26 23:10:20 +01:00
Oliver Kieselbach 81c4806083 Hardening: re-architecture to use mTLS for complete agent communication (agent endpoints consolidated) 2026-02-26 23:02:50 +01:00
Oliver Kieselbach 64d631376e fix footer on mobile 2026-02-26 21:25:52 +01:00
Oliver Kieselbach ca9c084d23 fix Docs table of content rendering on mobile 2026-02-26 21:00:45 +01:00
Oliver Kieselbach c09c74f7aa added Docs link to public site navbar 2026-02-26 20:45:11 +01:00
Oliver Kieselbach fc7f73b301 add tenant existence and suspension gate to all agent endpoints
Unknown tenants and suspended tenants are now rejected as the very
first step in SecurityValidator before any certificate, rate-limit,
or hardware check is performed. Adds TryGetConfigurationAsync to
TenantConfigurationService (no auto-create, cache-aware) and inserts
the gate in ValidateRequestAsync — covering RegisterSession,
IngestEvents, GetAgentConfig, and GetDiagnosticsUploadUrl.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 19:07:57 +01:00
Oliver Kieselbach 2bdce7e2cd HelloDetector fix and Changelog/Known-Issues Updates 2026-02-26 18:39:46 +01:00
Oliver Kieselbach 646eb68d7b Added Modal Dialog for unsaved changes in Settings 2026-02-26 14:58:15 +01:00