- Add docs/wiki/ with 11 wiki pages (Home, Architecture, Database, AI-Providers, PII-Detection, IPC-Commands, CICD-Pipeline, Security-Model, Integrations, Development-Setup, Troubleshooting) - Add wiki-sync step to .woodpecker/test.yml: syncs docs/wiki/*.md to the Gogs wiki git repo on every push to master - Add Wiki Maintenance section to CLAUDE.md: code→wiki file mapping so Claude and contributors know which wiki page to update per change Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
7.3 KiB
Architecture
Overview
TFTSR uses a Tauri 2.x architecture: a Rust backend runs natively, and a React/TypeScript frontend runs in an embedded WebView. Communication between them happens exclusively via typed IPC (invoke()).
┌─────────────────────────────────────────┐
│ WebView (React) │
│ pages/ → stores/ → tauriCommands.ts │
└──────────────────┬──────────────────────┘
│ invoke() / IPC
┌──────────────────▼──────────────────────┐
│ Rust Backend (Tauri) │
│ commands/ → ai/ → pii/ → db/ → docs/ │
└─────────────────────────────────────────┘
│ │
SQLCipher reqwest
DB (AI APIs)
Backend — Rust
Entry point: src-tauri/src/lib.rs → run() initialises tracing, opens the DB, registers Tauri plugins, and calls generate_handler![] with all IPC commands.
Shared State
pub struct AppState {
pub db: Arc<Mutex<rusqlite::Connection>>,
pub settings: Arc<Mutex<AppSettings>>,
pub app_data_dir: PathBuf, // ~/.local/share/tftsr on Linux
}
All command handlers receive State<'_, AppState> as a Tauri-injected parameter. The Mutex must be released before any .await — holding a MutexGuard across an await point is a compile error because MutexGuard is not Send.
Module Layout
| Path | Responsibility |
|---|---|
lib.rs |
App entry, tracing init, DB setup, plugin registration, command handler list |
state.rs |
AppState struct |
commands/db.rs |
Issue CRUD, 5-Whys entries, timeline events |
commands/ai.rs |
analyze_logs, chat_message, list_providers |
commands/analysis.rs |
Log file upload, PII detection, redaction |
commands/docs.rs |
RCA and post-mortem generation, document export |
commands/system.rs |
Ollama management, hardware probe, settings, audit log |
commands/integrations.rs |
Confluence / ServiceNow / ADO — v0.2 stubs |
ai/provider.rs |
Provider trait + create_provider() factory |
pii/detector.rs |
Multi-pattern PII scanner with overlap resolution |
db/migrations.rs |
Versioned schema (10 migrations in _migrations table) |
db/models.rs |
All DB types — see IssueDetail note below |
docs/rca.rs + docs/postmortem.rs |
Markdown template builders |
audit/log.rs |
write_audit_event() — called before every external send |
Directory Structure
src-tauri/src/
├── lib.rs
├── main.rs
├── state.rs
├── ai/
│ ├── provider.rs # Provider trait + factory
│ ├── openai.rs
│ ├── anthropic.rs
│ ├── gemini.rs
│ ├── mistral.rs
│ └── ollama.rs
├── commands/
│ ├── db.rs
│ ├── ai.rs
│ ├── analysis.rs
│ ├── docs.rs
│ ├── system.rs
│ └── integrations.rs
├── pii/
│ ├── patterns.rs
│ ├── detector.rs
│ └── redactor.rs
├── db/
│ ├── connection.rs
│ ├── migrations.rs
│ └── models.rs
├── docs/
│ ├── rca.rs
│ ├── postmortem.rs
│ └── exporter.rs
├── audit/
│ └── log.rs
├── ollama/
│ ├── installer.rs
│ ├── manager.rs
│ ├── recommender.rs
│ └── hardware.rs
└── integrations/
├── confluence.rs
├── servicenow.rs
└── azuredevops.rs
Frontend — React/TypeScript
IPC layer: All Tauri invoke() calls are in src/lib/tauriCommands.ts. Every command has a typed wrapper. This is the single source of truth for the frontend API surface.
Stores (Zustand)
| Store | Persistence | Contents |
|---|---|---|
sessionStore.ts |
Not persisted (ephemeral) | currentIssue, messages, piiSpans, approvedRedactions, whyLevel (0–5), loading state |
settingsStore.ts |
localStorage as "tftsr-settings" |
AI providers, theme, Ollama URL, active provider |
historyStore.ts |
Not persisted (cache) | Past issues list, search query |
Page Flow
NewIssue → createIssueCmd → startSession(detail.issue) → navigate /issue/:id/triage
LogUpload → uploadLogFileCmd → detectPiiCmd → applyRedactionsCmd
Triage → chatMessageCmd loop → auto-detect why levels 1–5
Resolution → getIssueCmd → mark 5-Whys steps done
RCA → generateRcaCmd → DocEditor → exportDocumentCmd
Directory Structure
src/
├── main.tsx
├── App.tsx
├── components/
│ ├── ChatWindow.tsx
│ ├── TriageProgress.tsx
│ ├── PiiDiffViewer.tsx
│ ├── DocEditor.tsx
│ ├── HardwareReport.tsx
│ ├── ModelSelector.tsx
│ └── ui/index.tsx # Custom components (Card, Button, Input, etc.)
├── pages/
│ ├── Dashboard/
│ ├── NewIssue/
│ ├── LogUpload/
│ ├── Triage/
│ ├── Resolution/
│ ├── RCA/
│ ├── Postmortem/
│ ├── History/
│ └── Settings/
│ ├── AIProviders.tsx
│ ├── Ollama.tsx
│ ├── Integrations.tsx
│ └── Security.tsx
├── stores/
│ ├── sessionStore.ts
│ ├── settingsStore.ts
│ └── historyStore.ts
└── lib/
├── tauriCommands.ts
└── domainPrompts.ts
Key Type: IssueDetail
get_issue() returns a nested struct, not flat. Always use detail.issue.*:
pub struct IssueDetail {
pub issue: Issue, // Base fields (title, severity, etc.)
pub log_files: Vec<LogFile>,
pub resolution_steps: Vec<ResolutionStep>, // 5-Whys entries
pub conversations: Vec<AiConversation>,
}
Use detail.issue.title, not detail.title.
Application Startup Sequence
1. Initialize tracing (RUST_LOG controls level)
2. Determine data directory (~/.local/share/tftsr or TFTSR_DATA_DIR)
3. Open / create SQLite database (run migrations)
4. Create AppState (db + settings + app_data_dir)
5. Register Tauri plugins (stronghold, dialog, fs, shell, http, cli, updater)
6. Register all 39 IPC command handlers
7. Start WebView with React app
Data Flow
User Input
↓
[New Issue] ──── UUID assigned, stored in DB
↓
[Upload Log] ─── File read, SHA-256 hash computed, path stored
↓
[Detect PII] ─── 13 regex patterns applied, overlaps resolved
↓
[Review PII] ─── User approves/rejects each span
↓
[Apply Redactions] ─ Text rewritten, audit event logged
↓
[AI Chat] ──────── Domain system prompt injected
Redacted text sent to provider
Auto-detect why level from response
↓
[5-Whys] ───────── Answers stored as resolution_steps
↓
[Generate RCA] ─── Markdown from template + answers
↓
[Export] ────────── MD or PDF to user-chosen directory