From 665be36180693c67c43eabf0b1b7744f0c1e2e62 Mon Sep 17 00:00:00 2001 From: Gitea Actions Date: Sun, 5 Apr 2026 15:57:12 +0000 Subject: [PATCH] docs: sync from docs/wiki/ at commit 350013e0 --- AI-Providers.md | 35 ++++++++++++++++++++++------------- CICD-Pipeline.md | 15 +++++++++++---- Development-Setup.md | 5 +++-- Home.md | 6 +++--- PII-Detection.md | 11 ++++++----- Security-Model.md | 31 ++++++++++++++++++++++++------- 6 files changed, 69 insertions(+), 34 deletions(-) diff --git a/AI-Providers.md b/AI-Providers.md index 91880a0..74c1f40 100644 --- a/AI-Providers.md +++ b/AI-Providers.md @@ -55,13 +55,21 @@ Covers: OpenAI, Azure OpenAI, LM Studio, vLLM, **LiteLLM (AWS Bedrock)**, and an |-------|-------| | `config.name` | `"gemini"` | | URL | `https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent` | -| Auth | API key as `?key=` query parameter | +| Auth | `x-goog-api-key: ` header | | Max tokens | 4096 | **Models:** `gemini-2.0-flash`, `gemini-2.0-pro`, `gemini-1.5-pro`, `gemini-1.5-flash` --- +## Transport Security Notes + +- Provider clients use TLS certificate verification via `reqwest` +- Provider calls are configured with explicit request timeouts to avoid indefinite hangs +- Credentials are sent in headers (not URL query strings) + +--- + ### 4. Mistral AI | Field | Value | @@ -113,7 +121,7 @@ The domain prompt is injected as the first `system` role message in every new co --- -## 6. Custom Provider (MSI GenAI & Others) +## 6. Custom Provider (Custom REST & Others) **Status:** ✅ **Implemented** (v0.2.6) @@ -137,25 +145,26 @@ Standard OpenAI `/chat/completions` endpoint with Bearer authentication. --- -### Format: MSI GenAI +### Format: Custom REST **Motorola Solutions Internal GenAI Service** — Enterprise AI platform with centralized cost tracking and model access. | Field | Value | |-------|-------| | `config.provider_type` | `"custom"` | -| `config.api_format` | `"msi_genai"` | +| `config.api_format` | `"custom_rest"` | | API URL | `https://genai-service.commandcentral.com/app-gateway` (prod)
`https://genai-service.stage.commandcentral.com/app-gateway` (stage) | | Auth Header | `x-msi-genai-api-key` | | Auth Prefix | `` (empty - no Bearer prefix) | | Endpoint Path | `` (empty - URL includes full path `/api/v2/chat`) | -**Available Models:** +**Available Models (dropdown in Settings):** - `VertexGemini` — Gemini 2.0 Flash (Private/GCP) - `Claude-Sonnet-4` — Claude Sonnet 4 (Public/Anthropic) - `ChatGPT4o` — GPT-4o (Public/OpenAI) - `ChatGPT-5_2-Chat` — GPT-4.5 (Public/OpenAI) -- See [GenAI API User Guide](../GenAI%20API%20User%20Guide.md) for full model list +- Full list is sourced from [GenAI API User Guide](../GenAI%20API%20User%20Guide.md) +- Includes a `Custom model...` option to manually enter any model ID **Request Format:** ```json @@ -187,9 +196,9 @@ Standard OpenAI `/chat/completions` endpoint with Bearer authentication. **Configuration (Settings → AI Providers → Add Provider):** ``` -Name: MSI GenAI +Name: Custom REST (MSI GenAI) Type: Custom -API Format: MSI GenAI +API Format: Custom REST API URL: https://genai-service.stage.commandcentral.com/app-gateway Model: VertexGemini API Key: (your MSI GenAI API key from portal) @@ -208,13 +217,13 @@ Auth Prefix: (leave empty) | Error | Cause | Solution | |-------|-------|----------| | 403 Forbidden | Invalid API key or insufficient permissions | Verify key in MSI GenAI portal, check model access | -| Missing `userId` field | Configuration not saved | Ensure UI shows User ID field when `api_format=msi_genai` | +| Missing `userId` field | Configuration not saved | Ensure UI shows User ID field when `api_format=custom_rest` | | No conversation history | `sessionId` not persisted | Session ID stored in `ProviderConfig.session_id` — currently per-provider, not per-conversation | **Implementation Details:** -- Backend: `src-tauri/src/ai/openai.rs::chat_msi_genai()` +- Backend: `src-tauri/src/ai/openai.rs::chat_custom_rest()` - Schema: `src-tauri/src/state.rs::ProviderConfig` (added `user_id`, `api_format`, custom auth fields) -- Frontend: `src/pages/Settings/AIProviders.tsx` (conditional UI for MSI GenAI) +- Frontend: `src/pages/Settings/AIProviders.tsx` (conditional UI for Custom REST + model dropdown) - CSP whitelist: `https://genai-service.stage.commandcentral.com` and production domain --- @@ -228,9 +237,9 @@ All providers support the following optional configuration fields (v0.2.6+): | `custom_endpoint_path` | `Option` | Override endpoint path | `/chat/completions` | | `custom_auth_header` | `Option` | Custom auth header name | `Authorization` | | `custom_auth_prefix` | `Option` | Prefix before API key | `Bearer ` | -| `api_format` | `Option` | API format (`openai` or `msi_genai`) | `openai` | +| `api_format` | `Option` | API format (`openai` or `custom_rest`) | `openai` | | `session_id` | `Option` | Session ID for stateful APIs | None | -| `user_id` | `Option` | User ID for cost tracking (MSI GenAI) | None | +| `user_id` | `Option` | User ID for cost tracking (Custom REST MSI contract) | None | **Backward Compatibility:** All fields are optional and default to OpenAI-compatible behavior. Existing provider configurations are unaffected. diff --git a/CICD-Pipeline.md b/CICD-Pipeline.md index 59b4ed4..e36fd2d 100644 --- a/CICD-Pipeline.md +++ b/CICD-Pipeline.md @@ -29,7 +29,7 @@ macOS runner runs jobs **directly on the host** (no Docker container) — macOS ## Test Pipeline (`.woodpecker/test.yml`) -**Triggers:** Every push and pull request to any branch. +**Triggers:** Pull requests only. ``` Pipeline steps: @@ -65,20 +65,27 @@ steps: --- -## Release Pipeline (`.gitea/workflows/release.yml`) +## Release Pipeline (`.gitea/workflows/auto-tag.yml`) -**Triggers:** Git tags matching `v*` +**Triggers:** Pushes to `master` (auto-tag), then release build/upload jobs run after `autotag`. + +Auto tags are created by `.gitea/workflows/auto-tag.yml` using `git tag` + `git push`. +Release jobs are executed in the same workflow and depend on `autotag` completion. ``` Jobs (run in parallel): build-linux-amd64 → cargo tauri build (x86_64-unknown-linux-gnu) → {.deb, .rpm, .AppImage} uploaded to Gitea release + → fails fast if no Linux artifacts are produced build-windows-amd64 → cargo tauri build (x86_64-pc-windows-gnu) via mingw-w64 → {.exe, .msi} uploaded to Gitea release + → fails fast if no Windows artifacts are produced build-linux-arm64 → cargo tauri build (aarch64-unknown-linux-gnu) → {.deb, .rpm, .AppImage} uploaded to Gitea release + → fails fast if no Linux artifacts are produced build-macos-arm64 → cargo tauri build (aarch64-apple-darwin) — runs on local Mac → {.dmg} uploaded to Gitea release + → existing same-name assets are deleted before upload (rerun-safe) → unsigned; after install run: xattr -cr /Applications/TFTSR.app ``` @@ -102,7 +109,7 @@ the repo directly within its commands (using `http://172.0.0.29:3000`, accessibl the local machine) and uploads its artifacts inline. The `upload-release` step (amd64) handles amd64 + windows artifacts only. -**Clone override (release.yml — amd64 workspace):** +**Clone override (auto-tag.yml — amd64 workspace):** ```yaml clone: diff --git a/Development-Setup.md b/Development-Setup.md index e338eeb..4bf779c 100644 --- a/Development-Setup.md +++ b/Development-Setup.md @@ -35,7 +35,8 @@ npm install --legacy-peer-deps | Variable | Default | Purpose | |----------|---------|---------| | `TFTSR_DATA_DIR` | Platform data dir | Override DB location | -| `TFTSR_DB_KEY` | `dev-key-change-in-prod` | DB encryption key (required in production) | +| `TFTSR_DB_KEY` | _(none)_ | DB encryption key (required in release builds) | +| `TFTSR_ENCRYPTION_KEY` | _(none)_ | Credential encryption key (required in release builds) | | `RUST_LOG` | `info` | Tracing verbosity: `debug`, `info`, `warn`, `error` | Application data is stored at: @@ -120,7 +121,7 @@ cargo tauri build # Outputs: .deb, .rpm, .AppImage (Linux) ``` -Release builds enable **SQLCipher AES-256** encryption. Set `TFTSR_DB_KEY` before building. +Release builds enforce secure key configuration. Set both `TFTSR_DB_KEY` and `TFTSR_ENCRYPTION_KEY` before building. --- diff --git a/Home.md b/Home.md index ebbe7fe..fe15cb0 100644 --- a/Home.md +++ b/Home.md @@ -1,6 +1,6 @@ -# TFTSR — IT Triage & RCA Desktop Application +# Troubleshooting and RCA Assistant -**TFTSR** is a secure desktop application for guided IT incident triage, root cause analysis (RCA), and post-mortem documentation. Built with Tauri 2.x (Rust + WebView) and React 18. +**Troubleshooting and RCA Assistant** is a secure desktop application for guided IT incident triage, root cause analysis (RCA), and post-mortem documentation. Built with Tauri 2.x (Rust + WebView) and React 18. **CI:** ![build](http://172.0.0.29:3000/sarman/tftsr-devops_investigation/actions/workflows/test.yml/badge.svg) — rustfmt · clippy · 64 Rust tests · tsc · vitest — all green @@ -25,7 +25,7 @@ - **5-Whys AI Triage** — Interactive guided root cause analysis via multi-turn AI chat - **PII Auto-Redaction** — Detects and redacts sensitive data before any AI send - **Multi-Provider AI** — OpenAI, Anthropic Claude, Google Gemini, Mistral, AWS Bedrock (via LiteLLM), MSI GenAI (Motorola internal), local Ollama (fully offline) -- **Custom Provider Support** — Flexible authentication (Bearer, custom headers) and API formats (OpenAI-compatible, MSI GenAI) +- **Custom Provider Support** — Flexible authentication (Bearer, custom headers) and API formats (OpenAI-compatible, Custom REST) - **External Integrations** — Confluence, ServiceNow, Azure DevOps with OAuth2 PKCE flows - **SQLCipher AES-256** — All issue history and credentials encrypted at rest - **RCA + Post-Mortem Generation** — Auto-populated Markdown templates, exportable as MD/PDF diff --git a/PII-Detection.md b/PII-Detection.md index f084269..f44ef90 100644 --- a/PII-Detection.md +++ b/PII-Detection.md @@ -10,7 +10,7 @@ Before any text is sent to an AI provider, TFTSR scans it for personally identif 1. Upload log file ↓ 2. detect_pii(log_file_id) - → Scans content with 13 regex patterns + → Scans content with PII regex patterns (including hostname + expanded card brands) → Resolves overlapping matches (longest wins) → Returns Vec with byte offsets + replacements ↓ @@ -24,7 +24,7 @@ Before any text is sent to an AI provider, TFTSR scans it for personally identif 5. Redacted text safe to send to AI ``` -## Detection Patterns (13 Types) +## Detection Patterns | Type | Replacement | Pattern notes | |------|-------------|---------------| @@ -33,13 +33,13 @@ Before any text is sent to an AI provider, TFTSR scans it for personally identif | `ApiKey` | `[ApiKey]` | `api_key=`, `apikey=`, `access_token=` + 16+ char value | | `Password` | `[Password]` | `password=`, `passwd=`, `pwd=` + non-whitespace value | | `Ssn` | `[SSN]` | `\b\d{3}-\d{2}-\d{4}\b` | -| `CreditCard` | `[CreditCard]` | Visa/MC/Amex Luhn-format numbers | +| `CreditCard` | `[CreditCard]` | Visa/MC/Amex/Discover/JCB/Diners patterns | | `Email` | `[Email]` | RFC-compliant email addresses | | `MacAddress` | `[MAC]` | `XX:XX:XX:XX:XX:XX` and `XX-XX-XX-XX-XX-XX` | | `Ipv6` | `[IPv6]` | Full and compressed IPv6 addresses | | `Ipv4` | `[IPv4]` | Standard dotted-quad notation | | `PhoneNumber` | `[Phone]` | US and international phone formats | -| `Hostname` | _(patterns.rs)_ | Configurable hostname patterns | +| `Hostname` | `[Hostname]` | FQDN/hostname detection for internal names | | `UrlCredentials` | _(covered by UrlWithCredentials)_ | | ## Overlap Resolution @@ -71,7 +71,7 @@ pub struct PiiSpan { pub pii_type: PiiType, pub start: usize, // byte offset in original text pub end: usize, - pub original_value: String, + pub original: String, pub replacement: String, // e.g., "[IPv4]" } ``` @@ -111,3 +111,4 @@ write_audit_event( - Only the redacted text is sent to AI providers - The SHA-256 hash in the audit log allows integrity verification - If redaction is skipped (no PII detected), the audit log still records the send +- Stored `pii_spans.original_value` metadata is cleared after redaction is finalized diff --git a/Security-Model.md b/Security-Model.md index ebc1c53..6a8af50 100644 --- a/Security-Model.md +++ b/Security-Model.md @@ -18,20 +18,25 @@ Production builds use SQLCipher: - **Cipher:** AES-256-CBC - **KDF:** PBKDF2-HMAC-SHA512, 256,000 iterations - **HMAC:** HMAC-SHA512 -- **Page size:** 4096 bytes +- **Page size:** 16384 bytes - **Key source:** `TFTSR_DB_KEY` environment variable Debug builds use plain SQLite (no encryption) for developer convenience. -> ⚠️ **Never** use the default key (`dev-key-change-in-prod`) in a production environment. +Release builds now fail startup if `TFTSR_DB_KEY` is missing or empty. --- -## API Key Storage (Stronghold) +## Credential Encryption -AI provider API keys are stored in `tauri-plugin-stronghold` — an encrypted vault backed by the [IOTA Stronghold](https://github.com/iotaledger/stronghold.rs) library. +Integration tokens are encrypted with AES-256-GCM before persistence: +- **Key source:** `TFTSR_ENCRYPTION_KEY` (required in release builds) +- **Key derivation:** SHA-256 hash of key material to a fixed 32-byte AES key +- **Nonce:** Cryptographically secure random nonce per encryption -The vault is initialized with a password-derived key using Argon2. API keys are never written to disk in plaintext or to the SQLite database. +Release builds fail secure operations if `TFTSR_ENCRYPTION_KEY` is unset or empty. + +The Stronghold plugin remains enabled and now uses a per-installation salt derived from the app data directory path hash instead of a fixed static salt. --- @@ -46,6 +51,7 @@ log file → detect_pii() → user approves spans → apply_redactions() → AI - Original text **never leaves the machine** - Only the redacted version is transmitted - The SHA-256 hash of the redacted text is recorded in the audit log for integrity verification +- `pii_spans.original_value` is cleared after redaction to avoid retaining raw detected secrets in storage - See [PII Detection](PII-Detection) for the full list of detected patterns --- @@ -66,6 +72,14 @@ write_audit_event( The audit log is stored in the encrypted SQLite database. It cannot be deleted through the UI. +### Tamper Evidence + +`audit_log` entries now include: +- `prev_hash` — hash of the previous audit entry +- `entry_hash` — SHA-256 hash of current entry payload + `prev_hash` + +This creates a hash chain and makes post-hoc modification detectable. + **Audit entry fields:** - `action` — what was done - `entity_type` — type of record involved @@ -84,7 +98,7 @@ Defined in `src-tauri/capabilities/default.json`: |--------|-------------------| | `dialog` | `allow-open`, `allow-save` | | `fs` | `read-text`, `write-text`, `read`, `write`, `mkdir` — scoped to app dir and temp | -| `shell` | `allow-execute` — for running system commands | +| `shell` | `allow-open` only | | `http` | default — connect only to approved origins | --- @@ -109,7 +123,9 @@ HTTP is blocked by default. Only whitelisted HTTPS endpoints (and localhost for ## TLS -All outbound HTTP requests use `reqwest` with default TLS settings (TLS 1.2+ required). Certificate verification is enabled. No custom trust anchors are added. +All outbound HTTP requests use `reqwest` with certificate verification enabled and a request timeout configured for provider calls. + +CI/CD currently uses internal `http://` endpoints for self-hosted Gitea release automation on a trusted LAN. Recommended hardening: migrate runners and API calls to HTTPS with internal certificates. --- @@ -120,3 +136,4 @@ All outbound HTTP requests use `reqwest` with default TLS settings (TLS 1.2+ req - [ ] Does it store secrets? → Use Stronghold, not the SQLite DB - [ ] Does it need filesystem access? → Scope the fs capability - [ ] Does it need a new HTTP endpoint? → Add to CSP `connect-src` +- [ ] Does it add a new provider endpoint? → Avoid query-param secrets, use auth headers