391 lines
13 KiB
Markdown
391 lines
13 KiB
Markdown
|
|
# TFTSR — IT Triage & Root-Cause Analysis Desktop Application
|
||
|
|
|
||
|
|
## Implementation Plan
|
||
|
|
|
||
|
|
### Overview
|
||
|
|
|
||
|
|
TFTSR is a **desktop-first, offline-capable** application that helps IT teams
|
||
|
|
perform structured incident triage using the *5-Whys* methodology, backed by
|
||
|
|
pluggable AI providers (Ollama local, OpenAI, Anthropic, Mistral, Gemini).
|
||
|
|
It automates PII redaction, guides engineers through root-cause analysis, and
|
||
|
|
produces post-mortem documents (Markdown / PDF / DOCX).
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Architecture Decisions
|
||
|
|
|
||
|
|
| Area | Choice | Rationale |
|
||
|
|
|------|--------|-----------|
|
||
|
|
| Desktop framework | **Tauri 2.x** | Small binary, native webview, Rust backend for security |
|
||
|
|
| Frontend framework | **React 18** | Large ecosystem, component model fits wizard-style UX |
|
||
|
|
| State management | **Zustand** | Minimal boilerplate, TypeScript-friendly, no context nesting |
|
||
|
|
| Local database | **SQLCipher** (via `rusqlite` + `bundled-sqlcipher`) | Encrypted SQLite — secrets and PII at rest |
|
||
|
|
| Secret storage | **Tauri Stronghold** | OS-keychain-grade encrypted vault for API keys |
|
||
|
|
| AI providers | Ollama (local), OpenAI, Anthropic, Mistral, Gemini | User choice; local-first with cloud fallback |
|
||
|
|
| Unit tests (frontend) | **Vitest** | Fast, Vite-native, first-class TS support |
|
||
|
|
| E2E tests | **WebdriverIO + tauri-driver** | Official Tauri E2E path, cross-platform |
|
||
|
|
| CI/CD | **Woodpecker CI** (Gogs at `172.0.0.29:3000`) | Self-hosted, Docker-native, YAML pipelines |
|
||
|
|
| Bundling | Vite 6 | Dev server + production build, used by Tauri CLI |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Directory Structure
|
||
|
|
|
||
|
|
```
|
||
|
|
tftsr/
|
||
|
|
├── .woodpecker/
|
||
|
|
│ ├── test.yml # lint + unit tests on push / PR
|
||
|
|
│ └── release.yml # multi-platform build on tag
|
||
|
|
├── cli/
|
||
|
|
│ ├── package.json
|
||
|
|
│ └── src/
|
||
|
|
│ └── main.ts # minimal CLI entry point
|
||
|
|
├── src/ # React frontend
|
||
|
|
│ ├── assets/
|
||
|
|
│ ├── components/
|
||
|
|
│ │ ├── common/ # Button, Card, Modal, DropZone …
|
||
|
|
│ │ ├── dashboard/ # IssueList, StatsCards
|
||
|
|
│ │ ├── triage/ # WhyStep, ChatBubble, ProgressBar
|
||
|
|
│ │ ├── rca/ # DocEditor, ExportBar
|
||
|
|
│ │ ├── settings/ # ProviderForm, ThemeToggle
|
||
|
|
│ │ └── pii/ # PiiHighlighter, RedactionPreview
|
||
|
|
│ ├── hooks/ # useInvoke, useListener, useTheme …
|
||
|
|
│ ├── lib/
|
||
|
|
│ │ ├── tauriCommands.ts # typed invoke wrappers & TS types
|
||
|
|
│ │ └── utils.ts # date formatting, debounce, etc.
|
||
|
|
│ ├── pages/
|
||
|
|
│ │ ├── DashboardPage.tsx
|
||
|
|
│ │ ├── NewIssuePage.tsx
|
||
|
|
│ │ ├── TriagePage.tsx
|
||
|
|
│ │ ├── RcaPage.tsx
|
||
|
|
│ │ ├── LogViewerPage.tsx
|
||
|
|
│ │ └── SettingsPage.tsx
|
||
|
|
│ ├── stores/
|
||
|
|
│ │ ├── sessionStore.ts # current triage session state
|
||
|
|
│ │ └── settingsStore.ts # theme, providers, preferences
|
||
|
|
│ ├── App.tsx
|
||
|
|
│ └── main.tsx
|
||
|
|
├── src-tauri/
|
||
|
|
│ ├── Cargo.toml
|
||
|
|
│ ├── tauri.conf.json
|
||
|
|
│ ├── capabilities/
|
||
|
|
│ │ └── default.json
|
||
|
|
│ ├── icons/
|
||
|
|
│ ├── src/
|
||
|
|
│ │ ├── main.rs # Tauri entry point
|
||
|
|
│ │ ├── db.rs # SQLCipher connection & migrations
|
||
|
|
│ │ ├── commands/ # IPC command modules
|
||
|
|
│ │ │ ├── mod.rs
|
||
|
|
│ │ │ ├── issues.rs
|
||
|
|
│ │ │ ├── triage.rs
|
||
|
|
│ │ │ ├── logs.rs
|
||
|
|
│ │ │ ├── pii.rs
|
||
|
|
│ │ │ ├── rca.rs
|
||
|
|
│ │ │ ├── ai.rs
|
||
|
|
│ │ │ └── settings.rs
|
||
|
|
│ │ ├── ai/ # AI provider abstractions
|
||
|
|
│ │ │ ├── mod.rs
|
||
|
|
│ │ │ ├── ollama.rs
|
||
|
|
│ │ │ ├── openai_compat.rs
|
||
|
|
│ │ │ └── prompt_templates.rs
|
||
|
|
│ │ ├── pii/ # PII detection engine
|
||
|
|
│ │ │ ├── mod.rs
|
||
|
|
│ │ │ └── patterns.rs
|
||
|
|
│ │ └── export/ # Document export
|
||
|
|
│ │ ├── mod.rs
|
||
|
|
│ │ ├── markdown.rs
|
||
|
|
│ │ ├── pdf.rs
|
||
|
|
│ │ └── docx.rs
|
||
|
|
│ └── migrations/
|
||
|
|
│ └── 001_init.sql
|
||
|
|
├── tests/
|
||
|
|
│ ├── unit/
|
||
|
|
│ │ ├── setup.ts
|
||
|
|
│ │ ├── pii.test.ts
|
||
|
|
│ │ ├── sessionStore.test.ts
|
||
|
|
│ │ └── settingsStore.test.ts
|
||
|
|
│ └── e2e/
|
||
|
|
│ ├── wdio.conf.ts
|
||
|
|
│ ├── helpers/
|
||
|
|
│ │ └── app.ts
|
||
|
|
│ └── specs/
|
||
|
|
│ ├── onboarding.spec.ts
|
||
|
|
│ ├── log-upload.spec.ts
|
||
|
|
│ ├── triage-flow.spec.ts
|
||
|
|
│ └── rca-export.spec.ts
|
||
|
|
├── package.json
|
||
|
|
├── tsconfig.json
|
||
|
|
├── vite.config.ts
|
||
|
|
└── PLAN.md # ← this file
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Database Schema (SQLCipher)
|
||
|
|
|
||
|
|
All tables live in a single encrypted `tftsr.db` file under the Tauri
|
||
|
|
app-data directory.
|
||
|
|
|
||
|
|
### 1. `issues`
|
||
|
|
```sql
|
||
|
|
CREATE TABLE issues (
|
||
|
|
id TEXT PRIMARY KEY,
|
||
|
|
title TEXT NOT NULL,
|
||
|
|
domain TEXT NOT NULL CHECK(domain IN
|
||
|
|
('linux','windows','network','k8s','db','virt','hw','obs')),
|
||
|
|
status TEXT NOT NULL DEFAULT 'open'
|
||
|
|
CHECK(status IN ('open','triaging','resolved','closed')),
|
||
|
|
severity TEXT CHECK(severity IN ('p1','p2','p3','p4')),
|
||
|
|
created_at INTEGER NOT NULL,
|
||
|
|
updated_at INTEGER NOT NULL
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. `triage_messages`
|
||
|
|
```sql
|
||
|
|
CREATE TABLE triage_messages (
|
||
|
|
id TEXT PRIMARY KEY,
|
||
|
|
issue_id TEXT NOT NULL REFERENCES issues(id),
|
||
|
|
role TEXT NOT NULL CHECK(role IN ('user','assistant','system')),
|
||
|
|
content TEXT NOT NULL,
|
||
|
|
why_level INTEGER NOT NULL DEFAULT 0,
|
||
|
|
created_at INTEGER NOT NULL
|
||
|
|
);
|
||
|
|
CREATE INDEX idx_triage_msg_issue ON triage_messages(issue_id);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. `log_files`
|
||
|
|
```sql
|
||
|
|
CREATE TABLE log_files (
|
||
|
|
id TEXT PRIMARY KEY,
|
||
|
|
issue_id TEXT NOT NULL REFERENCES issues(id),
|
||
|
|
filename TEXT NOT NULL,
|
||
|
|
content TEXT NOT NULL,
|
||
|
|
mime_type TEXT,
|
||
|
|
size_bytes INTEGER,
|
||
|
|
created_at INTEGER NOT NULL
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4. `pii_spans`
|
||
|
|
```sql
|
||
|
|
CREATE TABLE pii_spans (
|
||
|
|
id TEXT PRIMARY KEY,
|
||
|
|
log_file_id TEXT NOT NULL REFERENCES log_files(id),
|
||
|
|
pii_type TEXT NOT NULL,
|
||
|
|
start_pos INTEGER NOT NULL,
|
||
|
|
end_pos INTEGER NOT NULL,
|
||
|
|
original TEXT NOT NULL,
|
||
|
|
replacement TEXT NOT NULL
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 5. `rca_documents`
|
||
|
|
```sql
|
||
|
|
CREATE TABLE rca_documents (
|
||
|
|
id TEXT PRIMARY KEY,
|
||
|
|
issue_id TEXT NOT NULL REFERENCES issues(id) UNIQUE,
|
||
|
|
content TEXT NOT NULL DEFAULT '',
|
||
|
|
format TEXT NOT NULL DEFAULT 'markdown',
|
||
|
|
created_at INTEGER NOT NULL,
|
||
|
|
updated_at INTEGER NOT NULL
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 6. `ai_providers`
|
||
|
|
```sql
|
||
|
|
CREATE TABLE ai_providers (
|
||
|
|
id TEXT PRIMARY KEY,
|
||
|
|
name TEXT NOT NULL UNIQUE,
|
||
|
|
api_url TEXT NOT NULL,
|
||
|
|
model TEXT NOT NULL,
|
||
|
|
created_at INTEGER NOT NULL
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 7. `settings`
|
||
|
|
```sql
|
||
|
|
CREATE TABLE settings (
|
||
|
|
key TEXT PRIMARY KEY,
|
||
|
|
value TEXT NOT NULL
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
### 8. `export_history`
|
||
|
|
```sql
|
||
|
|
CREATE TABLE export_history (
|
||
|
|
id TEXT PRIMARY KEY,
|
||
|
|
issue_id TEXT NOT NULL REFERENCES issues(id),
|
||
|
|
format TEXT NOT NULL CHECK(format IN ('md','pdf','docx')),
|
||
|
|
file_path TEXT NOT NULL,
|
||
|
|
created_at INTEGER NOT NULL
|
||
|
|
);
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## IPC Command Interface
|
||
|
|
|
||
|
|
All frontend ↔ backend communication goes through Tauri's `invoke()`.
|
||
|
|
|
||
|
|
### Issue commands
|
||
|
|
| Command | Payload | Returns |
|
||
|
|
|---------|---------|---------|
|
||
|
|
| `create_issue` | `{ title, domain, severity }` | `Issue` |
|
||
|
|
| `list_issues` | `{ status?, domain? }` | `Issue[]` |
|
||
|
|
| `get_issue` | `{ id }` | `Issue` |
|
||
|
|
| `update_issue` | `{ id, title?, status?, severity? }` | `Issue` |
|
||
|
|
| `delete_issue` | `{ id }` | `void` |
|
||
|
|
|
||
|
|
### Triage commands
|
||
|
|
| Command | Payload | Returns |
|
||
|
|
|---------|---------|---------|
|
||
|
|
| `send_triage_message` | `{ issueId, content, whyLevel }` | `TriageMessage` (assistant reply) |
|
||
|
|
| `get_triage_history` | `{ issueId }` | `TriageMessage[]` |
|
||
|
|
| `set_why_level` | `{ issueId, level }` | `void` |
|
||
|
|
|
||
|
|
### Log commands
|
||
|
|
| Command | Payload | Returns |
|
||
|
|
|---------|---------|---------|
|
||
|
|
| `upload_log` | `{ issueId, filename, content }` | `LogFile` |
|
||
|
|
| `list_logs` | `{ issueId }` | `LogFile[]` |
|
||
|
|
| `delete_log` | `{ id }` | `void` |
|
||
|
|
|
||
|
|
### PII commands
|
||
|
|
| Command | Payload | Returns |
|
||
|
|
|---------|---------|---------|
|
||
|
|
| `detect_pii` | `{ logFileId }` | `PiiDetectionResult` |
|
||
|
|
| `apply_redactions` | `{ logFileId, spanIds }` | `string` (redacted text) |
|
||
|
|
|
||
|
|
### RCA / Export commands
|
||
|
|
| Command | Payload | Returns |
|
||
|
|
|---------|---------|---------|
|
||
|
|
| `generate_rca` | `{ issueId }` | `RcaDocument` |
|
||
|
|
| `update_rca` | `{ id, content }` | `RcaDocument` |
|
||
|
|
| `export_document` | `{ issueId, format }` | `string` (file path) |
|
||
|
|
|
||
|
|
### AI / Settings commands
|
||
|
|
| Command | Payload | Returns |
|
||
|
|
|---------|---------|---------|
|
||
|
|
| `test_provider` | `{ name, apiUrl, apiKey?, model }` | `{ ok, message }` |
|
||
|
|
| `save_provider` | `{ provider }` | `void` |
|
||
|
|
| `get_settings` | `{}` | `Settings` |
|
||
|
|
| `update_settings` | `{ key, value }` | `void` |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## CI/CD Approach
|
||
|
|
|
||
|
|
### Infrastructure
|
||
|
|
- **Git server**: Gogs at `http://172.0.0.29:3000`
|
||
|
|
- **CI runner**: Woodpecker CI with Docker executor
|
||
|
|
- **Artifacts**: Uploaded to Gogs releases via API
|
||
|
|
|
||
|
|
### Pipelines
|
||
|
|
|
||
|
|
| Pipeline | Trigger | Steps |
|
||
|
|
|----------|---------|-------|
|
||
|
|
| `.woodpecker/test.yml` | push, PR | `rustfmt` check → Clippy → Rust tests → TS typecheck → Vitest → coverage (main only) |
|
||
|
|
| `.woodpecker/release.yml` | `v*` tag | Build linux-amd64 → Build linux-arm64 → Upload to Gogs release |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Security Implementation
|
||
|
|
|
||
|
|
1. **Database encryption** — SQLCipher with a key derived from Tauri Stronghold.
|
||
|
|
2. **API key storage** — Stronghold vault, never stored in plaintext.
|
||
|
|
3. **PII redaction** — Regex + heuristic engine runs before any text leaves the device.
|
||
|
|
4. **CSP** — Strict Content-Security-Policy in `tauri.conf.json`; only allowlisted AI API origins.
|
||
|
|
5. **Least-privilege capabilities** — `capabilities/default.json` grants only required Tauri permissions.
|
||
|
|
6. **No remote code** — All assets bundled; no CDN scripts.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Testing Strategy
|
||
|
|
|
||
|
|
| Layer | Tool | Location | What it covers |
|
||
|
|
|-------|------|----------|----------------|
|
||
|
|
| Rust unit | `cargo test` | `src-tauri/src/**` | DB operations, PII regex, AI prompt building |
|
||
|
|
| Frontend unit | Vitest | `tests/unit/` | Stores, command wrappers, component logic |
|
||
|
|
| E2E | WebdriverIO + tauri-driver | `tests/e2e/` | Full user flows: onboarding, triage, export |
|
||
|
|
| Lint | `rustfmt` + Clippy + `tsc --noEmit` | CI | Code style, type safety |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Implementation Phases
|
||
|
|
|
||
|
|
### Phase 1 — Project Scaffold & CI [IN PROGRESS]
|
||
|
|
- [x] Initialise repo with Tauri 2.x + React 18 + Vite
|
||
|
|
- [x] Configure `tauri.conf.json` and capabilities
|
||
|
|
- [x] Set up Woodpecker CI pipelines (`test.yml`, `release.yml`)
|
||
|
|
- [x] Write Vitest setup and mock harness
|
||
|
|
- [x] Write initial unit tests (PII, sessionStore, settingsStore)
|
||
|
|
- [x] Write E2E scaffolding (wdio config, helpers, skeleton specs)
|
||
|
|
- [x] Create CLI stub (`cli/`)
|
||
|
|
- [ ] Verify CI green on first push
|
||
|
|
|
||
|
|
### Phase 2 — Database & Migrations
|
||
|
|
- [ ] Integrate `rusqlite` + `bundled-sqlcipher`
|
||
|
|
- [ ] Write `001_init.sql` migration with all 8 tables
|
||
|
|
- [ ] Implement migration runner in `db.rs`
|
||
|
|
- [ ] Unit-test DB operations
|
||
|
|
|
||
|
|
### Phase 3 — Stronghold Integration
|
||
|
|
- [ ] Add `tauri-plugin-stronghold`
|
||
|
|
- [ ] Store/retrieve DB encryption key
|
||
|
|
- [ ] Store/retrieve AI API keys
|
||
|
|
- [ ] Test key lifecycle
|
||
|
|
|
||
|
|
### Phase 4 — Issue CRUD
|
||
|
|
- [ ] Implement `commands/issues.rs`
|
||
|
|
- [ ] Wire IPC commands
|
||
|
|
- [ ] Build `DashboardPage` and `NewIssuePage` UI
|
||
|
|
- [ ] Unit-test issue store + commands
|
||
|
|
|
||
|
|
### Phase 5 — Log Ingestion & PII Detection
|
||
|
|
- [ ] Implement `commands/logs.rs` and `pii/` engine
|
||
|
|
- [ ] Build `DropZone` + `PiiHighlighter` components
|
||
|
|
- [ ] Write comprehensive PII regex tests
|
||
|
|
- [ ] E2E: log upload flow
|
||
|
|
|
||
|
|
### Phase 6 — AI Provider Abstraction
|
||
|
|
- [ ] Implement `ai/ollama.rs` and `ai/openai_compat.rs`
|
||
|
|
- [ ] Build `SettingsPage` provider configuration UI
|
||
|
|
- [ ] `test_provider` command with connectivity check
|
||
|
|
- [ ] Unit-test prompt templates
|
||
|
|
|
||
|
|
### Phase 7 — 5-Whys Triage Engine
|
||
|
|
- [ ] Implement `commands/triage.rs` with streaming support
|
||
|
|
- [ ] Build `TriagePage` with `WhyStep` + `ChatBubble`
|
||
|
|
- [ ] Wire progress bar to why-level state
|
||
|
|
- [ ] E2E: full triage flow
|
||
|
|
|
||
|
|
### Phase 8 — RCA Document Generation
|
||
|
|
- [ ] Implement `commands/rca.rs` + `generate_rca`
|
||
|
|
- [ ] Build `RcaPage` with `DocEditor`
|
||
|
|
- [ ] Test RCA generation with mock AI responses
|
||
|
|
|
||
|
|
### Phase 9 — Document Export
|
||
|
|
- [ ] Implement `export/markdown.rs`, `pdf.rs`, `docx.rs`
|
||
|
|
- [ ] Build export bar with format selection
|
||
|
|
- [ ] Test each export format
|
||
|
|
- [ ] E2E: export flow
|
||
|
|
|
||
|
|
### Phase 10 — Polish & Accessibility
|
||
|
|
- [ ] Dark/light theme toggle
|
||
|
|
- [ ] Keyboard navigation
|
||
|
|
- [ ] Loading states and error boundaries
|
||
|
|
- [ ] Responsive layout adjustments
|
||
|
|
|
||
|
|
### Phase 11 — Release Pipeline Validation
|
||
|
|
- [ ] Tag `v0.1.0-alpha`
|
||
|
|
- [ ] Verify Woodpecker builds Linux amd64 + arm64
|
||
|
|
- [ ] Verify artifacts upload to Gogs release
|
||
|
|
- [ ] Smoke-test installed packages
|
||
|
|
|
||
|
|
### Phase 12 — Documentation & Handoff
|
||
|
|
- [ ] Write user-facing README
|
||
|
|
- [ ] Document AI provider setup guide
|
||
|
|
- [ ] Record architecture decision log
|
||
|
|
- [ ] Final CI badge + release notes
|