# FlightDeck Architecture Diagrams Visual architecture documentation for FlightDeck โ€” a personal work-intelligence dashboard that connects to Microsoft 365 via WorkIQ and Microsoft Copilot. --- ## 1. System Architecture How FlightDeck's Electron app, WorkIQ CLI, Microsoft Copilot, and Microsoft 365 connect end-to-end. ```mermaid graph TB subgraph User["๐Ÿ‘ค User"] UI["FlightDeck Desktop App"] end subgraph Electron["Electron Application"] direction TB subgraph Renderer["Renderer Process ยท Browser"] direction LR App["app.js
Tab routing & init"] Events["events.js
DOM event wiring"] Monitor["monitor-engine.js
30s tick scheduler"] ScannerEng["scanner-engine.js
Multi-scanner scheduler"] Demo["demo.js
Fixture mode (?demo=1)"] Prompts["prompts.js
Prompt editor & cache"] Parser["json-parser.js
LLM output โ†’ JSON"] State["state.js
electron-store persistence"] Utils["utils.js
escapeHtml, URL safety"] subgraph Models["Models"] Item["item.js
Unified item model"] Scanner["scanner.js
Scanner definitions"] Radar["radar.js"] Tracking["tracking.js"] Briefing["briefing.js"] end subgraph Views["Renderers"] KPI["kpi.js"] RadarR["radar.js"] TrackingR["tracking.js"] BriefingR["briefing.js"] ScannerR["scanner.js"] ActionsR["actions.js"] HistoryR["history.js"] end end subgraph Shared["Shared"] Contract["ipc-contract.js
Channel name constants"] end Preload["preload.js
contextBridge โ†’ window.workiq
(20 methods)"] subgraph Main["Main Process ยท Node.js"] direction LR Index["index.js
App lifecycle & tray"] IPC["ipc-handlers.js
IPC channel routing"] PTY["pty-bridge.js
node-pty spawn"] Store["store.js
electron-store
(data + cold)"] PopoutIPC["ipc/tracker-popout.js
Pop-out window mgmt"] PromptFiles["prompts/
radar-scan.md
briefing.md
day-briefing.md
scanner-template.md"] end end subgraph External["External Services"] WorkIQ["WorkIQ CLI
workiq.exe"] Copilot["Microsoft Copilot
AI Engine"] M365["Microsoft 365
Email ยท Teams ยท Calendar ยท Docs"] end UI --> Renderer Renderer <-->|"IPC invoke/send"| Preload Preload <-->|"ipcRenderer โ†” ipcMain"| Main Shared --- Preload Shared --- Main IPC -->|"read prompt files"| PromptFiles IPC -->|"spawn PTY"| PTY IPC -->|"read/write"| Store IPC -->|"pop-out"| PopoutIPC PTY -->|"workiq ask -q 'prompt'"| WorkIQ WorkIQ -->|"Copilot API"| Copilot Copilot <-->|"Graph API"| M365 WorkIQ -->|"JSON + natural language"| PTY PTY -->|"ANSI-stripped output"| IPC ``` **Key architecture decisions:** - **Electron with context isolation** โ€” The renderer is sandboxed. All Node.js access goes through `preload.js`, which exposes exactly 20 whitelisted methods via `window.workiq`. - **Shared IPC contract** โ€” All channel name strings are defined once in `shared/ipc-contract.js` and imported by both the main process and preload script, eliminating string duplication. - **electron-store for persistence** โ€” State is backed by a JSON file on disk (`electron-store`) rather than `localStorage`. A separate cold store holds archived items to keep the active store lean. - **PTY bridge, not HTTP** โ€” WorkIQ is a CLI tool, not an API. FlightDeck uses `node-pty` to spawn pseudo-terminal sessions, which handles interactive prompts (like EULA acceptance) and streaming output. - **Multi-scanner architecture** โ€” Users can create multiple named scanners, each with independent prompts, schedules, and configurations. A dedicated scanner engine runs alongside the single-item monitor engine. - **No framework, no bundler** โ€” The renderer is vanilla HTML/CSS/JS. This keeps the build chain minimal and the codebase auditable. --- ## 2. WorkIQ Call Pipeline The complete data flow when FlightDeck sends a prompt to WorkIQ and renders the result. ```mermaid sequenceDiagram actor User participant R as Renderer
(Browser) participant P as Preload
(contextBridge) participant M as Main Process
(ipc-handlers) participant PTY as PTY Bridge
(node-pty) participant WIQ as WorkIQ CLI participant AI as Microsoft Copilot participant M365 as Microsoft 365 User->>R: Click "Refresh" or scheduled tick fires R->>R: Load prompt template
(radar-scan.md / briefing.md) R->>R: Append JSON schema suffix
(constants.js) R->>P: window.workiq.ask(prompt) P->>M: ipcRenderer.invoke('ask-workiq', prompt) M->>PTY: runWorkiqCommand(prompt) PTY->>PTY: Resolve workiq.exe path
(bundled โ†’ global โ†’ JS fallback) PTY->>WIQ: pty.spawn(workiq, ['ask', '-q', prompt]) Note over PTY,WIQ: 5-minute timeout guard WIQ->>AI: Forward prompt to Copilot AI->>M365: Query Graph API
(mail, chats, calendar, files) M365-->>AI: M365 signals & data AI-->>WIQ: Grounded AI response
(JSON + citations) WIQ-->>PTY: Raw CLI output
(with ANSI codes) PTY->>PTY: Strip ANSI escapes
Filter prompt lines PTY-->>M: {success: true, answer: cleanedOutput} M-->>P: IPC response P-->>R: Promise resolves R->>R: json-parser.js:
Extract JSON from fenced blocks R->>R: models/: Normalize payload,
compute KPIs, detect changes R->>R: renderers/: Update DOM
(cards, KPIs, badges) R->>R: state.js: Persist to localStorage R-->>User: Updated dashboard ``` **What makes this interesting:** - **Prompt engineering is the product** โ€” Each feature (Radar, Briefings, Tracking) is driven by a carefully crafted markdown prompt that instructs Copilot on what to look for, how to classify it, and what JSON schema to return. - **In-app prompt editor** โ€” Users can customize the radar and briefing prompts directly in the app. FlightDeck persists customizations to localStorage and falls back to the bundled defaults. - **Robust JSON extraction** โ€” LLM output isn't pure JSON. The parser handles fenced code blocks, mixed text, Unicode artifacts, trailing commas, and ANSI contamination. --- ## 3. Feature Modes & Prompt Flow How FlightDeck's three core features connect to prompt templates and M365 data. ```mermaid flowchart LR subgraph Prompts["Prompt Templates"] RS["radar-scan.md
Scan M365 signals,
classify by urgency"] ST["scanner-template.md
Scanner default prompt
with signal focus"] BF["briefing.md
Meeting prep with
talk track & risks"] DB["day-briefing.md
Morning summary
of full workday"] end subgraph Features["FlightDeck Features"] direction TB ScanF["๐Ÿ” Scanners
Named scan definitions
Custom prompts + schedules"] RadarF["๐Ÿ“ก Radar
Inbound signal scan
Critical ยท Elevated ยท Observe"] TrackF["๐Ÿ“Œ Tracking
Monitor items over time
Interval ยท Weekly ยท One-time"] BriefF["๐Ÿ“‹ Briefings
AI meeting prep
+ My Day overview"] end subgraph DataSources["Microsoft 365 Signals via WorkIQ + Copilot"] Email["๐Ÿ“ง Email"] Teams["๐Ÿ’ฌ Teams Chats"] Calendar["๐Ÿ“… Calendar"] Docs["๐Ÿ“„ Documents"] end RS --> RadarF ST --> ScanF BF --> BriefF DB --> BriefF RS -.->|"Custom prompt
via in-app editor"| RadarF ST -.->|"User-defined
scanner prompt"| ScanF BF -.->|"Custom prompt
via in-app editor"| BriefF DataSources -->|"Graph API โ†’ Copilot โ†’ WorkIQ"| Prompts ScanF -->|"Discovered items"| RadarF ScanF -->|"Auto-monitor
(severity threshold)"| TrackF RadarF -->|"Track Item"| TrackF RadarF -->|"Open Source Link"| Email RadarF -->|"Open Source Link"| Teams TrackF -->|"Desktop notification ๐Ÿ””
on substantive change"| User([๐Ÿ‘ค User]) BriefF -->|"Talk track + follow-ups"| User ``` | Feature | Prompt | What it does | |---------|--------|--------------| | **Scanners** | `scanner-template.md` (default) or user-defined | Named scan definitions with independent prompts and schedules. Each scanner discovers items, deduplicates across scanners, and can auto-monitor new items based on severity thresholds. | | **Radar** | `radar-scan.md` | Scans email, Teams, calendar, and documents for signals that need attention. Classifies each as Critical, Elevated, or Observe. Returns evidence links with deep URLs back to the source. | | **Tracking** | Dynamic per-item | Monitors a specific item on a user-configured schedule. Includes the last 2 update summaries for de-duplication so the LLM only reports *new* information. | | **Briefings** | `briefing.md` / `day-briefing.md` | Generates meeting prep (key updates, decisions needed, risks, talk track, follow-ups) or a full "My Day" morning briefing. | --- ## 4. Monitoring Engine โ€” Scheduled Task Update Cycle The background engine that keeps tracked items up to date. ```mermaid flowchart TD Start([Monitor Engine Tick
every 30 seconds]) --> Check{Any tracked items
due now?} Check -->|No| Sleep([Wait for next tick]) Check -->|Yes| BuildPrompt[Build prompt with:
โ€ข Item context & title
โ€ข Last 2 update summaries
โ€ข De-duplication instructions] BuildPrompt --> CallWorkIQ[window.workiq.ask โ€” prompt โ€”
IPC โ†’ PTY โ†’ WorkIQ CLI] CallWorkIQ --> Parse[json-parser.js:
Extract JSON from response] Parse --> HasNew{LLM says
hasNewInfo?} HasNew -->|No / false| Silent[Silent log
Preserve existing fields
Update lastRunAt only] HasNew -->|Yes| UpdateFields[Update item fields:
summary, status, severity,
owner, evidence links] UpdateFields --> ComputeSig[Compute field-level
signature hash] ComputeSig --> SigMatch{Signature changed
substantively?} SigMatch -->|No| LogOnly[Log link-only or
cosmetic change] SigMatch -->|Yes| Notify[Desktop notification ๐Ÿ””
+ Update badge
+ History entry] Silent --> Schedule LogOnly --> Schedule Notify --> Schedule Schedule[Compute next run time
based on schedule type] --> Persist[Save to localStorage
Broadcast state-changed
to pop-out windows] Persist --> Render[Re-render tracking cards] Render --> NextItem{More due items?} NextItem -->|Yes| BuildPrompt NextItem -->|No| Sleep ``` **How change detection works:** 1. Each tracked item has a **signature hash** computed from its status, severity, summary, and evidence links. 2. After the LLM returns an update, FlightDeck computes a new signature and compares it to the previous one. 3. Only **substantive changes** (status, severity, or meaningful summary differences) trigger desktop notifications. Link-only or cosmetic changes are logged silently. 4. The LLM's `hasNewInfo` flag provides a first-pass filter โ€” if the LLM says nothing changed, FlightDeck preserves all existing fields to prevent signature drift from rephrasing. --- ## 5. Security Model How FlightDeck isolates the renderer from Node.js and validates external content. ```mermaid graph TB subgraph Security["Security Boundary"] direction TB subgraph RendererSandbox["Renderer Sandbox"] CSP["CSP: default-src 'self'
No inline scripts
No external resources"] NoNode["nodeIntegration: false
contextIsolation: true"] Escape["All LLM output HTML-escaped
before DOM insertion"] end Bridge["contextBridge ยท preload.js
20 whitelisted methods"] subgraph MainTrust["Main Process ยท Trusted"] URLCheck["URL validation
HTTPS only"] NavGuard["External navigation
intercepted โ†’ system browser"] PTYIsolation["PTY process isolation
5-min timeout kill"] end end RendererSandbox <-->|"window.workiq.*
20 whitelisted methods"| Bridge Bridge <-->|"Named IPC channels"| MainTrust ``` | Layer | Measure | |-------|---------| | **Content Security Policy** | `default-src 'self'; style-src 'self'; script-src 'self'` โ€” no inline scripts, no external resources | | **Context isolation** | Renderer cannot access Node.js APIs directly | | **Node integration** | Explicitly disabled | | **IPC surface** | 20 named channels exposed through `preload.js`, defined centrally in `shared/ipc-contract.js` | | **External navigation** | All navigation attempts intercepted and opened in the system browser, never in the Electron window | | **URL validation** | Non-HTTPS schemes rejected before opening; generic M365 root URLs filtered out | | **LLM output sanitization** | All AI-generated text is HTML-escaped before DOM insertion to prevent injection | | **PTY timeout** | 5-minute hard timeout kills hung WorkIQ processes | --- ## 6. Responsible AI (RAI) Notes | Concern | Mitigation | |---------|------------| | **Data access scope** | FlightDeck accesses only the signed-in user's own M365 data via WorkIQ + Microsoft Copilot. No cross-tenant or cross-user data access. Requires tenant admin consent. | | **AI grounding** | All Copilot responses are grounded in the user's real M365 signals (email, Teams, calendar, documents). FlightDeck prompts explicitly request citations and evidence links back to source content. | | **Hallucination mitigation** | JSON schema constraints in prompts enforce structured output. The parser validates response structure before rendering. Evidence links are validated against known URL patterns (Outlook, Teams, SharePoint). | | **No data storage beyond device** | All user data is stored locally in `localStorage` and a window-state JSON file. No data is sent to external servers beyond the existing WorkIQ โ†’ Copilot โ†’ Graph API path. | | **Prompt transparency** | Users can view and edit the exact prompts sent to Copilot via the in-app prompt editor. No hidden instructions. | | **LLM output sanitization** | All AI-generated text is HTML-escaped before rendering. No raw HTML or script content from AI responses reaches the DOM. | | **EULA and consent** | WorkIQ requires explicit EULA acceptance. FlightDeck auto-detects when the EULA needs re-acceptance and surfaces the "Enable WorkIQ" flow. |