From 10b14a2ffef863518843ea35dfcb907b01f516a5 Mon Sep 17 00:00:00 2001 From: Shaun Arman Date: Tue, 31 Mar 2026 12:25:56 -0500 Subject: [PATCH] docs: sync wiki with latest changes including LiteLLM + Bedrock guide --- AI-Providers.md | 6 +- CICD-Pipeline.md | 243 +++++++++++++++++------- Development-Setup.md | 2 +- Home.md | 27 ++- LiteLLM-Bedrock-Setup.md | 396 +++++++++++++++++++++++++++++++++++++++ Troubleshooting.md | 204 ++++++++++++-------- 6 files changed, 722 insertions(+), 156 deletions(-) create mode 100644 LiteLLM-Bedrock-Setup.md diff --git a/AI-Providers.md b/AI-Providers.md index d419b32..462f57b 100644 --- a/AI-Providers.md +++ b/AI-Providers.md @@ -19,7 +19,7 @@ pub trait Provider { ### 1. OpenAI-Compatible -Covers: OpenAI, Azure OpenAI, LM Studio, vLLM, and any OpenAI-API-compatible endpoint. +Covers: OpenAI, Azure OpenAI, LM Studio, vLLM, **LiteLLM (AWS Bedrock)**, and any OpenAI-API-compatible endpoint. | Field | Value | |-------|-------| @@ -30,7 +30,9 @@ Covers: OpenAI, Azure OpenAI, LM Studio, vLLM, and any OpenAI-API-compatible end **Models:** `gpt-4o`, `gpt-4o-mini`, `gpt-4-turbo` -**Custom endpoint:** Set `config.base_url` to any OpenAI-compatible API (e.g., LM Studio at `http://localhost:1234/v1`). +**Custom endpoint:** Set `config.base_url` to any OpenAI-compatible API: +- LM Studio: `http://localhost:1234/v1` +- **LiteLLM (AWS Bedrock):** `http://localhost:8000/v1` — See [LiteLLM + Bedrock Setup](LiteLLM-Bedrock-Setup) for full configuration guide --- diff --git a/CICD-Pipeline.md b/CICD-Pipeline.md index d868d03..59b4ed4 100644 --- a/CICD-Pipeline.md +++ b/CICD-Pipeline.md @@ -4,10 +4,26 @@ | Component | URL | Notes | |-----------|-----|-------| -| Gogs | `http://172.0.0.29:3000` / `https://gogs.tftsr.com` | Git server, version 0.14 | -| Woodpecker CI (direct) | `http://172.0.0.29:8084` | v0.15.4 | -| Woodpecker CI (proxy) | `http://172.0.0.29:8085` | nginx with custom login page | -| PostgreSQL (Gogs DB) | Container: `gogs_postgres_db` | DB: `gogsdb`, User: `gogs` | +| Gitea | `https://gogs.tftsr.com` / `http://172.0.0.29:3000` | Git server (migrated from Gogs 0.14) | +| Woodpecker CI (direct) | `http://172.0.0.29:8084` | v2.x | +| Woodpecker CI (proxy) | `http://172.0.0.29:8085` | nginx reverse proxy | +| PostgreSQL (Gitea DB) | Container: `gogs_postgres_db` | DB: `gogsdb`, User: `gogs` | + +### CI Agents + +| Agent | Platform | Host | Purpose | +|-------|----------|------|---------| +| `gitea_act_runner_amd64` (Docker) | `linux-amd64` | 172.0.0.29 | Native x86_64 — test builds + amd64/windows release | +| `act_runner` (systemd) | `linux-arm64` | 172.0.0.29 | Native aarch64 — arm64 release builds | +| `act_runner` (launchd) | `macos-arm64` | sarman's local Mac | Native Apple Silicon — macOS `.dmg` release builds | + +Agent labels configured in `~/.config/act_runner/config.yaml`: +```yaml +runner: + labels: + - "macos-arm64:host" +``` +macOS runner runs jobs **directly on the host** (no Docker container) — macOS SDK cannot run in Docker. --- @@ -19,140 +35,227 @@ Pipeline steps: 1. rust-fmt-check → cargo fmt --check 2. rust-clippy → cargo clippy -- -D warnings - 3. rust-tests → cargo test + 3. rust-tests → cargo test (64 tests) 4. frontend-typecheck → npx tsc --noEmit - 5. frontend-tests → npm run test:run (Vitest) + 5. frontend-tests → npm run test:run (13 Vitest tests) ``` **Docker images used:** - `rust:1.88-slim` — Rust steps (minimum for cookie_store + time + darling) - `node:22-alpine` — Frontend steps -**System dependencies installed in CI (Rust steps):** -``` -libwebkit2gtk-4.1-dev, libssl-dev, libgtk-3-dev, libsoup-3.0-dev, -librsvg2-dev, libglib2.0-dev -``` - -**Pipeline YAML format (Woodpecker 0.15.4 — legacy MAP format):** +**Pipeline YAML format (Woodpecker 2.x — steps list format):** ```yaml clone: git: image: woodpeckerci/plugin-git network_mode: gogs_default # requires repo_trusted=1 environment: - - CI_REPO_CLONE_URL=http://gogs_app:3000/sarman/tftsr-devops_investigation.git + - CI_REPO_CLONE_URL=http://gitea_app:3000/sarman/tftsr-devops_investigation.git -pipeline: - step-name: # KEY = step name (MAP, not list!) +steps: + - name: step-name # LIST format (- name:) image: rust:1.88-slim commands: - cargo test ``` -> ⚠️ **Do NOT** use the newer `steps:` list format — Woodpecker 0.15.4 uses the Drone-legacy map format. +> ⚠️ Woodpecker 2.x uses the `steps:` list format. The legacy `pipeline:` map format from +> Woodpecker 0.15.4 is no longer supported. --- -## Release Pipeline (`.woodpecker/release.yml`) +## Release Pipeline (`.gitea/workflows/release.yml`) **Triggers:** Git tags matching `v*` ``` -Pipeline steps: - 1. build-linux-amd64 → cargo tauri build (x86_64-unknown-linux-gnu) - 2. build-linux-arm64 → cargo tauri build (aarch64-unknown-linux-gnu, cross-compile) - 3. upload-release → Create Gogs release + upload artifacts via API +Jobs (run in parallel): + build-linux-amd64 → cargo tauri build (x86_64-unknown-linux-gnu) + → {.deb, .rpm, .AppImage} uploaded to Gitea release + build-windows-amd64 → cargo tauri build (x86_64-pc-windows-gnu) via mingw-w64 + → {.exe, .msi} uploaded to Gitea release + build-linux-arm64 → cargo tauri build (aarch64-unknown-linux-gnu) + → {.deb, .rpm, .AppImage} uploaded to Gitea release + build-macos-arm64 → cargo tauri build (aarch64-apple-darwin) — runs on local Mac + → {.dmg} uploaded to Gitea release + → unsigned; after install run: xattr -cr /Applications/TFTSR.app +``` + +**Per-step agent routing (Woodpecker 2.x labels):** + +```yaml +steps: + - name: build-linux-amd64 + labels: + platform: linux/amd64 # → woodpecker_agent on 172.0.0.29 + + - name: build-linux-arm64 + labels: + platform: linux/arm64 # → woodpecker-agent.service on local arm64 machine +``` + +**Multi-agent workspace isolation:** + +Steps routed to different agents do **not** share a workspace. The arm64 step clones +the repo directly within its commands (using `http://172.0.0.29:3000`, accessible from +the local machine) and uploads its artifacts inline. The `upload-release` step (amd64) +handles amd64 + windows artifacts only. + +**Clone override (release.yml — amd64 workspace):** + +```yaml +clone: + git: + image: alpine/git + network_mode: gogs_default + commands: + - git init -b master + - git remote add origin http://gitea_app:3000/sarman/tftsr-devops_investigation.git + - git fetch --depth=1 origin +refs/tags/${CI_COMMIT_TAG}:refs/tags/${CI_COMMIT_TAG} + - git checkout ${CI_COMMIT_TAG} +``` + +**Windows cross-compile environment:** +```yaml +environment: + TARGET: x86_64-pc-windows-gnu + CC_x86_64_pc_windows_gnu: x86_64-w64-mingw32-gcc + CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER: x86_64-w64-mingw32-gcc ``` **Artifacts per platform:** - Linux amd64: `.deb`, `.rpm`, `.AppImage` -- Linux arm64: `.deb`, `.AppImage` +- Windows amd64: `.exe` (NSIS installer), `.msi` +- Linux arm64: `.deb`, `.rpm`, `.AppImage` -**Gogs Release API:** +**Upload step (requires gogs_default network for amd64, host IP for arm64):** +```yaml +# amd64 upload step +upload-release: + image: curlimages/curl:latest + labels: + platform: linux/amd64 + network_mode: gogs_default + secrets: [GOGS_TOKEN] +``` + +The `GOGS_TOKEN` Woodpecker secret must be created via the Woodpecker UI or API after +migration. The secret name stays `GOGS_TOKEN` for pipeline compatibility. + +**Gitea Release API (replaces Gogs API — same endpoints, different container name):** ```bash # Create release -POST $API/repos/sarman/tftsr-devops_investigation/releases +POST http://gitea_app:3000/api/v1/repos/sarman/tftsr-devops_investigation/releases Authorization: token $GOGS_TOKEN # Upload artifact -POST $API/repos/sarman/tftsr-devops_investigation/releases/{id}/assets +POST http://gitea_app:3000/api/v1/repos/sarman/tftsr-devops_investigation/releases/{id}/assets ``` -The `GOGS_TOKEN` is stored as a Woodpecker secret. +From the arm64 agent (local machine), use `http://172.0.0.29:3000/api/v1` instead. + +--- + +## Multi-File Pipeline Support (Woodpecker 2.x) + +Woodpecker 2.x supports multiple pipeline files in the `.woodpecker/` directory. +All `.yml` files are evaluated on every trigger; `when:` conditions control which +pipelines actually run. + +Current files: +- `.woodpecker/test.yml` — runs on every push/PR +- `.woodpecker/release.yml` — runs on `v*` tags only + +No DB config path switching needed (unlike Woodpecker 0.15.4). --- ## Webhook Configuration -**Hook ID:** 6 (in Gogs) -**Events:** `create`, `push`, `pull_request` -**URL:** `http://172.0.0.29:8084/hook?access_token=` +**Woodpecker 2.x with Gitea OAuth2:** -**JWT signing:** -- Algorithm: HS256 -- Secret: `repo_hash` value from Woodpecker DB (`dK8zFWtAu67qfKd3Et6N8LptqTmedumJ`) -- Payload: `{"text":"sarman/tftsr-devops_investigation","type":"hook"}` +After migration, Woodpecker 2.x registers webhooks automatically when a repo is +activated via the UI. No manual JWT-signed webhook setup required. -> ⚠️ JWT has an `iat` claim. If it's stale, regenerate it. +1. Log in at `http://172.0.0.29:8085` via Gitea OAuth2 +2. Add repo `sarman/tftsr-devops_investigation` +3. Woodpecker creates webhook in Gitea automatically --- -## Woodpecker DB State +## Branch Protection -SQLite at `/docker_mounts/woodpecker/data/woodpecker.sqlite` (on host `172.0.0.29`). +Master branch is protected: all changes require a PR. -Key values: ```sql --- User -SELECT user_token FROM users WHERE user_login='sarman'; --- Should be: a6b3ee679fe9601e983f1a0c01493758d6033ef3 +-- Gitea branch protection (via psql on gogs_postgres_db container) +-- Check protection +SELECT name, protected, require_pull_request FROM protect_branch WHERE repo_id=42; --- Repo -SELECT repo_active, repo_trusted, repo_config_path, repo_hash -FROM repos WHERE repo_full_name='sarman/tftsr-devops_investigation'; --- repo_active=1, repo_trusted=1 --- repo_config_path='.woodpecker/test.yml' --- repo_hash='dK8zFWtAu67qfKd3Et6N8LptqTmedumJ' +-- Temporarily disable for urgent fixes (restore immediately after!) +UPDATE protect_branch SET protected=false WHERE repo_id=42 AND name='master'; +-- ... push ... +UPDATE protect_branch SET protected=true, require_pull_request=true WHERE repo_id=42 AND name='master'; ``` --- ## Known Issues & Fixes -### Webhook JWT Must Use `?access_token=` -`token.ParseRequest()` in Woodpecker 0.15.4 does **not** read `?token=` URL params. Use `?access_token=` instead. +### Step Containers Cannot Reach `gitea_app` +Default Docker bridge containers cannot resolve `gitea_app` or reach `172.0.0.29:3000` +(host firewall). Fix: use `network_mode: gogs_default` in any step that needs Gitea +access. Requires `repo_trusted=1`. -### JWT Signed with `repo_hash` (Not User Hash) -Hook JWT must be signed with the `repo_hash` value, not the user's hash. +### `CI=woodpecker` Rejected by Tauri CLI +Woodpecker sets `CI=woodpecker`; `cargo tauri build` expects a boolean. Fix: prefix with +`CI=true cargo tauri build`. -### Directory-Based Config Not Supported -Woodpecker 0.15.4 only supports a **single config file**. Set `repo_config_path = .woodpecker/test.yml` in the Woodpecker DB. The `.woodpecker/` directory approach requires v2.x+. - -### Step Containers Network Isolation -Pipeline step containers run on the default Docker bridge and cannot resolve `gogs_app` hostname. Fix: set `network_mode: gogs_default` in the clone section (requires `repo_trusted=1`). - -### Empty Clone URL Bug -Woodpecker 0.15.4's `go-gogs-client` `PayloadRepo` struct lacks `CloneURL`/`SSHURL` fields, so `build_remote` is always empty from Gogs push payloads. Fix: override the clone URL via `CI_REPO_CLONE_URL` environment variable. - -### Gogs Token Authentication -The `sha1` field in Gogs token create API response **is** the actual bearer token (not a hash). Use it directly: -``` -Authorization: token +### Agent Stalls After Server Restart +After restarting the Woodpecker server, the agent may enter a loop cleaning up orphaned +containers and stop picking up new builds. Fix: +```bash +docker rm -f $(docker ps -aq --filter 'name=0_') +docker volume rm $(docker volume ls -q | grep '0_') +docker restart woodpecker_agent ``` -### Gogs SPA Login Field Mismatch -Gogs 0.14 SPA login form uses `login=` field; the Gogs backend reads `username=`. A custom login page is served by nginx at `/login`. +### Windows DLL Export Ordinal Too Large +`/usr/bin/x86_64-w64-mingw32-ld: error: export ordinal too large: 106290` -### Gogs OAuth2 Limitation -Gogs 0.14 has no OAuth2 provider support, blocking upgrade to Woodpecker 2.x. +Fix: `src-tauri/.cargo/config.toml`: +```toml +[target.x86_64-pc-windows-gnu] +rustflags = ["-C", "link-arg=-Wl,--exclude-all-symbols"] +``` + +### GOGS_TOKEN Secret Must Be Recreated After Migration +After migrating from Woodpecker 0.15.4 to 2.x, recreate the `GOGS_TOKEN` secret: +1. Log in to Gitea, create a new API token under Settings → Applications +2. In Woodpecker UI → Repository → Secrets, add secret `GOGS_TOKEN` with the token value --- -## Gogs PostgreSQL Access +## Gitea PostgreSQL Access ```bash docker exec gogs_postgres_db psql -U gogs -d gogsdb -c "SELECT id, lower_name FROM repository;" ``` -> Database name is `gogsdb`, not `gogs`. +> Database name is `gogsdb` (unchanged from Gogs migration). + +--- + +## Migration Notes (Gogs 0.14 → Gitea) + +Gitea auto-migrates the Gogs PostgreSQL schema on first start. Users, repos, teams, and +issues are preserved. API tokens stored in the DB are also migrated but should be +regenerated for security. + +Key changes after migration: +- Container name: `gogs_app` → `gitea_app` +- Config dir: `/data/gitea` (was `/data/gogs` inside container, same host volume) +- Repo dir: `gogs-repositories` → `gitea-repositories` (renamed on host during migration) +- OAuth2 provider: Gitea now supports OAuth2 (Woodpecker 2.x uses this for login) +- Woodpecker 2.x multi-file pipeline support enabled (no more single config file limitation) diff --git a/Development-Setup.md b/Development-Setup.md index 2e3983d..e338eeb 100644 --- a/Development-Setup.md +++ b/Development-Setup.md @@ -90,7 +90,7 @@ npm run test:coverage npx tsc --noEmit ``` -Current test status: **13/13 frontend tests passing**, Rust tests passing. +Current test status: **13/13 frontend tests passing**, **64/64 Rust tests passing**. --- diff --git a/Home.md b/Home.md index f390893..d5af4c3 100644 --- a/Home.md +++ b/Home.md @@ -2,6 +2,8 @@ **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. +**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 + ## Quick Navigation | Topic | Description | @@ -10,9 +12,10 @@ | [Development Setup](wiki/Development-Setup) | Prerequisites, commands, environment | | [Database](wiki/Database) | Schema, migrations, encryption | | [AI Providers](wiki/AI-Providers) | Supported providers and configuration | +| [LiteLLM + Bedrock Setup](wiki/LiteLLM-Bedrock-Setup) | AWS Bedrock integration via LiteLLM proxy | | [PII Detection](wiki/PII-Detection) | Patterns, redaction flow, security | | [IPC Commands](wiki/IPC-Commands) | Full list of Tauri backend commands | -| [CI/CD Pipeline](wiki/CICD-Pipeline) | Woodpecker CI + Gogs setup | +| [CI/CD Pipeline](wiki/CICD-Pipeline) | Gitea Actions setup, multi-platform builds, act_runner config | | [Security Model](wiki/Security-Model) | Encryption, audit trail, capabilities | | [Integrations](wiki/Integrations) | Confluence, ServiceNow, Azure DevOps (v0.2) | | [Troubleshooting](wiki/Troubleshooting) | Known issues and fixes | @@ -21,22 +24,30 @@ - **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, local Ollama (fully offline) +- **Multi-Provider AI** — OpenAI, Anthropic Claude, Google Gemini, Mistral, AWS Bedrock (via LiteLLM), local Ollama (fully offline) - **SQLCipher AES-256** — All issue history encrypted at rest - **RCA + Post-Mortem Generation** — Auto-populated Markdown templates, exportable as MD/PDF - **Ollama Management** — Hardware detection, model recommendations, in-app model management - **Audit Trail** — Every external data send logged with SHA-256 hash - **Domain-Specific Prompts** — 8 IT domains: Linux, Windows, Network, Kubernetes, Databases, Virtualization, Hardware, Observability +## Releases + +| Version | Status | Platforms | +|---------|--------|-----------| +| v0.1.1 | 🚀 Released | linux/amd64 · linux/arm64 · windows/amd64 (.deb, .rpm, .AppImage, .exe, .msi) | + +Download from [Releases](https://gogs.tftsr.com/sarman/tftsr-devops_investigation/releases). All builds are produced natively (no QEMU emulation). + ## Project Status | Phase | Status | |-------|--------| -| Phases 1–8 (Core) | ✅ Complete | -| Phase 9 (History/Search FTS) | 🔄 Partially integrated | +| Phases 1–8 (Core application) | ✅ Complete | +| Phase 9 (History/Search) | 🔲 Pending | | Phase 10 (Integrations) | 🕐 v0.2 stubs only | -| Phase 11 (CLI) | 🕐 Planned | -| Phase 12 (Release packaging) | 🔄 Linux done; macOS/Windows pending | +| Phase 11 (CI/CD) | ✅ Complete — Gitea Actions fully operational | +| Phase 12 (Release packaging) | ✅ linux/amd64 · linux/arm64 (native) · windows/amd64 | ## Tech Stack @@ -49,5 +60,5 @@ | Database | rusqlite + SQLCipher (AES-256) | | Secret storage | tauri-plugin-stronghold | | State | Zustand | -| Testing | Vitest (frontend) + `#[cfg(test)]` (Rust) | -| CI/CD | Woodpecker CI v0.15.4 + Gogs | +| Testing | Vitest (13 frontend) + `#[cfg(test)]` (64 Rust tests) | +| CI/CD | Gitea Actions (act_runner v0.3.1) + Gitea | diff --git a/LiteLLM-Bedrock-Setup.md b/LiteLLM-Bedrock-Setup.md new file mode 100644 index 0000000..6a291f7 --- /dev/null +++ b/LiteLLM-Bedrock-Setup.md @@ -0,0 +1,396 @@ +# LiteLLM + AWS Bedrock Setup + +This guide covers how to use **Claude via AWS Bedrock** with TFTSR through the LiteLLM proxy, providing an OpenAI-compatible API gateway. + +## Why LiteLLM + Bedrock? + +- **Enterprise AWS contracts** — Use existing AWS Bedrock credits instead of direct Anthropic API +- **Multiple AWS accounts** — Run personal and business Bedrock accounts simultaneously +- **OpenAI-compatible API** — Works with any tool expecting OpenAI's API format +- **Claude Code integration** — Reuse the same AWS credentials used by Claude Code CLI + +--- + +## Prerequisites + +- **AWS account** with Bedrock access and Claude models enabled +- **AWS credentials** configured (either default profile or named profile) +- **Python 3.8+** for LiteLLM installation + +--- + +## Installation + +### 1. Install LiteLLM + +```bash +pip install 'litellm[proxy]' +``` + +Verify installation: +```bash +litellm --version +``` + +--- + +## Basic Setup — Single AWS Account + +### 1. Create Configuration File + +Create `~/.litellm/config.yaml`: + +```yaml +model_list: + - model_name: bedrock-claude + litellm_params: + model: bedrock/us.anthropic.claude-sonnet-4-6 + aws_region_name: us-east-1 + # Uses default AWS credentials from ~/.aws/credentials + +general_settings: + master_key: sk-1234 # Any value — used for API authentication +``` + +### 2. Start LiteLLM Proxy + +```bash +# Run in background +nohup litellm --config ~/.litellm/config.yaml --port 8000 > ~/.litellm/litellm.log 2>&1 & + +# Verify it's running +ps aux | grep litellm +``` + +### 3. Test the Connection + +```bash +curl http://localhost:8000/v1/chat/completions \ + -H "Authorization: Bearer sk-1234" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "bedrock-claude", + "messages": [{"role": "user", "content": "Hello"}], + "max_tokens": 50 + }' +``` + +Expected response: +```json +{ + "id": "chatcmpl-...", + "model": "bedrock-claude", + "choices": [{ + "message": { + "role": "assistant", + "content": "Hello! How can I help you today?" + } + }] +} +``` + +### 4. Configure TFTSR + +In **Settings → AI Providers → Add Provider**: + +| Field | Value | +|-------|-------| +| Provider Name | OpenAI | +| Base URL | `http://localhost:8000/v1` | +| API Key | `sk-1234` (or your master_key from config) | +| Model | `bedrock-claude` | +| Display Name | `Bedrock Claude` | + +--- + +## Advanced Setup — Multiple AWS Accounts + +If you have **personal** and **business** Bedrock accounts, you can run both through the same LiteLLM instance. + +### 1. Configure AWS Profiles + +Ensure you have AWS profiles set up in `~/.aws/credentials`: + +```ini +[default] +aws_access_key_id = AKIA... +aws_secret_access_key = ... + +[ClaudeCodeLP] +aws_access_key_id = AKIA... +aws_secret_access_key = ... +``` + +Or if using credential process (like Claude Code does): + +```ini +# ~/.aws/config +[profile ClaudeCodeLP] +credential_process = /Users/${USER}/claude-code-with-bedrock/credential-process --profile ClaudeCodeLP +region = us-east-1 +``` + +### 2. Update Configuration File + +Edit `~/.litellm/config.yaml`: + +```yaml +model_list: + - model_name: bedrock-personal + litellm_params: + model: bedrock/us.anthropic.claude-sonnet-4-6 + aws_region_name: us-east-1 + # Uses default AWS credentials + + - model_name: bedrock-business + litellm_params: + model: bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0 + aws_region_name: us-east-1 + aws_profile_name: ClaudeCodeLP # Named profile for business account + +general_settings: + master_key: sk-1234 +``` + +### 3. Restart LiteLLM + +```bash +# Find and stop existing process +pkill -f "litellm --config" + +# Start with new config +nohup litellm --config ~/.litellm/config.yaml --port 8000 > ~/.litellm/litellm.log 2>&1 & +``` + +### 4. Verify Both Models + +```bash +# List available models +curl -s http://localhost:8000/v1/models \ + -H "Authorization: Bearer sk-1234" | python3 -m json.tool + +# Test personal account +curl -s http://localhost:8000/v1/chat/completions \ + -H "Authorization: Bearer sk-1234" \ + -H "Content-Type: application/json" \ + -d '{"model": "bedrock-personal", "messages": [{"role": "user", "content": "test"}]}' + +# Test business account +curl -s http://localhost:8000/v1/chat/completions \ + -H "Authorization: Bearer sk-1234" \ + -H "Content-Type: application/json" \ + -d '{"model": "bedrock-business", "messages": [{"role": "user", "content": "test"}]}' +``` + +### 5. Configure in TFTSR + +Add both models as separate providers: + +**Provider 1 — Personal:** +- Provider: OpenAI +- Base URL: `http://localhost:8000/v1` +- API Key: `sk-1234` +- Model: `bedrock-personal` +- Display Name: `Bedrock (Personal)` + +**Provider 2 — Business:** +- Provider: OpenAI +- Base URL: `http://localhost:8000/v1` +- API Key: `sk-1234` +- Model: `bedrock-business` +- Display Name: `Bedrock (Business)` + +--- + +## Claude Code Integration + +If you're using [Claude Code](https://claude.ai/claude-code) with AWS Bedrock, you can reuse the same AWS credentials. + +### 1. Check Claude Code Settings + +Read your Claude Code configuration: + +```bash +cat ~/.claude/settings.json +``` + +Look for: +- `AWS_PROFILE` environment variable (e.g., `ClaudeCodeLP`) +- `awsAuthRefresh` credential process path +- `AWS_REGION` setting + +### 2. Use Same Profile in LiteLLM + +In `~/.litellm/config.yaml`, add a model using the same profile: + +```yaml +model_list: + - model_name: claude-code-bedrock + litellm_params: + model: bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0 + aws_region_name: us-east-1 + aws_profile_name: ClaudeCodeLP # Same as Claude Code +``` + +Now both Claude Code and TFTSR use the same Bedrock account without duplicate credential management. + +--- + +## Available Claude Models on Bedrock + +| Model ID | Name | Context | Best For | +|----------|------|---------|----------| +| `us.anthropic.claude-sonnet-4-6` | Claude Sonnet 4.6 | 200k tokens | Most tasks, best quality | +| `us.anthropic.claude-sonnet-4-5-20250929-v1:0` | Claude Sonnet 4.5 | 200k tokens | High performance | +| `us.anthropic.claude-opus-4-6` | Claude Opus 4.6 | 200k tokens | Complex reasoning | +| `us.anthropic.claude-haiku-4-5-20251001` | Claude Haiku 4.5 | 200k tokens | Speed + cost optimization | + +Check your AWS Bedrock console for available models in your region. + +--- + +## Troubleshooting + +### Port Already in Use + +If port 8000 is occupied: + +```bash +# Find what's using the port +lsof -i :8000 + +# Use a different port +litellm --config ~/.litellm/config.yaml --port 8080 +``` + +Update the Base URL in TFTSR to match: `http://localhost:8080/v1` + +### AWS Credentials Not Found + +```bash +# Verify AWS CLI works +aws bedrock list-foundation-models --region us-east-1 + +# Test specific profile +aws bedrock list-foundation-models --region us-east-1 --profile ClaudeCodeLP +``` + +If AWS CLI fails, fix credentials first before debugging LiteLLM. + +### Model Not Available + +Error: `Model not found` or `Access denied` + +1. Check [AWS Bedrock Console](https://console.aws.amazon.com/bedrock/) → Model Access +2. Request access to Claude models if not enabled +3. Verify model ID matches exactly (case-sensitive) + +### LiteLLM Won't Start + +Check logs: +```bash +cat ~/.litellm/litellm.log +``` + +Common issues: +- Missing Python dependencies: `pip install 'litellm[proxy]' --upgrade` +- YAML syntax error: Validate with `python3 -c "import yaml; yaml.safe_load(open('${HOME}/.litellm/config.yaml'))"` + +--- + +## Auto-Start LiteLLM on Boot + +### macOS — LaunchAgent + +Create `~/Library/LaunchAgents/com.litellm.proxy.plist`: + +```xml + + + + + Label + com.litellm.proxy + ProgramArguments + + /opt/homebrew/bin/litellm + --config + /Users/${USER}/.litellm/config.yaml + --port + 8000 + + RunAtLoad + + KeepAlive + + StandardOutPath + /Users/${USER}/.litellm/litellm.log + StandardErrorPath + /Users/${USER}/.litellm/litellm-error.log + + +``` + +Load it: +```bash +launchctl load ~/Library/LaunchAgents/com.litellm.proxy.plist +``` + +### Linux — systemd + +Create `/etc/systemd/system/litellm.service`: + +```ini +[Unit] +Description=LiteLLM Proxy +After=network.target + +[Service] +Type=simple +User=${USER} +ExecStart=/usr/local/bin/litellm --config /home/${USER}/.litellm/config.yaml --port 8000 +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +``` + +Enable and start: +```bash +sudo systemctl enable litellm +sudo systemctl start litellm +sudo systemctl status litellm +``` + +--- + +## Cost Comparison + +| Provider | Model | Input (per 1M tokens) | Output (per 1M tokens) | +|----------|-------|----------------------|----------------------| +| **Anthropic Direct** | Claude Sonnet 4 | $3.00 | $15.00 | +| **AWS Bedrock** | Claude Sonnet 4 | $3.00 | $15.00 | + +Pricing is identical, but Bedrock provides: +- AWS consolidated billing +- AWS Credits applicability +- Integration with AWS services (S3, Lambda, etc.) +- Enterprise support contracts + +--- + +## Security Notes + +1. **Master Key** — The `master_key` in config is required but doesn't need to be complex since LiteLLM runs locally +2. **AWS Credentials** — Never commit `.aws/credentials` or credential process scripts to git +3. **Local Only** — LiteLLM proxy should only bind to `127.0.0.1` (localhost) — never expose to network +4. **Audit Logs** — TFTSR logs all AI requests with SHA-256 hashes in the audit table + +--- + +## References + +- [LiteLLM Documentation](https://docs.litellm.ai/) +- [AWS Bedrock Claude Models](https://docs.aws.amazon.com/bedrock/latest/userguide/models-claude.html) +- [Claude Code on Bedrock](https://docs.anthropic.com/claude/docs/claude-code) diff --git a/Troubleshooting.md b/Troubleshooting.md index 8dc17aa..0294b3c 100644 --- a/Troubleshooting.md +++ b/Troubleshooting.md @@ -1,70 +1,133 @@ # Troubleshooting -## CI/CD +## CI/CD — Gitea Actions -### Builds Not Triggering After Push +### Build Not Triggering After Push -**Cause:** Woodpecker 0.15.4 `token.ParseRequest()` does not read `?token=` URL params. - -**Fix:** Webhook URL must use `?access_token=` (not `?token=`). -``` -http://172.0.0.29:8084/hook?access_token= -``` - -Regenerate the JWT if it's stale (JWT has an `iat` claim): -```bash -# JWT payload: {"text":"sarman/tftsr-devops_investigation","type":"hook"} -# Signed with: repo_hash (dK8zFWtAu67qfKd3Et6N8LptqTmedumJ) -``` +**Check:** +1. Verify the workflow file exists in `.gitea/workflows/` on the pushed branch +2. Check the Actions tab at `http://172.0.0.29:3000/sarman/tftsr-devops_investigation/actions` +3. Confirm the act_runner is online: `docker logs gitea_act_runner_amd64 --since 5m` --- -### Pipeline Step Can't Reach Gogs +### Job Container Can't Reach Gitea (`172.0.0.29:3000` blocked) -**Cause:** Step containers run on the default Docker bridge, not on `gogs_default` network. +**Cause:** act_runner creates an isolated Docker network per job (when `container:` is specified). Traffic from the job container to `172.0.0.29:3000` is blocked by the host firewall. -**Fix:** Use `network_mode: gogs_default` in the clone section and ensure `repo_trusted=1`: -```bash -docker exec woodpecker_db sqlite3 /data/woodpecker.sqlite \ - "UPDATE repos SET repo_trusted=1 WHERE repo_full_name='sarman/tftsr-devops_investigation';" -``` +**Fix:** Ensure `container.network: host` is set in the act_runner config AND that `CONFIG_FILE=/data/config.yaml` is in the container's environment: ---- - -### Woodpecker Login Fails - -**Cause:** Gogs 0.14 SPA login form uses `login=` field; backend reads `username=`. - -**Fix:** Use the nginx proxy at `http://172.0.0.29:8085/login` which serves a corrected login form. - ---- - -### Empty Clone URL in Pipeline - -**Cause:** Woodpecker 0.15.4 `go-gogs-client` `PayloadRepo` struct is missing `CloneURL`. - -**Fix:** Override with `CI_REPO_CLONE_URL` environment variable in the clone section: ```yaml -clone: - git: - environment: - - CI_REPO_CLONE_URL=http://gogs_app:3000/sarman/tftsr-devops_investigation.git +# /docker_mounts/gitea/runner/amd64/config.yaml +container: + network: "host" ``` +```yaml +# docker-compose.yml for act-runner-amd64 +environment: + - CONFIG_FILE=/data/config.yaml +``` + +Also set `capacity: 1` — with capacity > 1, concurrent jobs may not get host networking: + +```yaml +runner: + capacity: 1 +``` + +Restart runner: `docker restart gitea_act_runner_amd64` + +--- + +### `Unable to locate package git` in Rust Job + +**Cause:** `rust:1.88-slim` has an empty apt package cache. + +**Fix:** Always run `apt-get update` before `apt-get install`: +```yaml +- name: Checkout + run: | + apt-get update -qq && apt-get install -y -qq git + git init + git remote add origin http://172.0.0.29:3000/sarman/tftsr-devops_investigation.git + git fetch --depth=1 origin $GITHUB_SHA + git checkout FETCH_HEAD +``` + +--- + +### `exec: "node": executable file not found in $PATH` + +**Cause:** `actions/checkout@v4` is a Node.js action. `rust:1.88-slim` and similar slim images don't have Node. + +**Fix:** Don't use `actions/checkout@v4` — use direct git commands instead (see above). + +--- + +### Job Skipped (status 6) on Tag Push + +**Cause:** Pattern matching issue with `on: push: tags:`. Use unquoted glob in the workflow: + +```yaml +# Correct +on: + push: + tags: + - v* +``` + +Also add `workflow_dispatch` for manual triggering during testing: +```yaml +on: + push: + tags: + - v* + workflow_dispatch: + inputs: + tag: + description: 'Release tag' + required: true +``` + +--- + +### `CI=woodpecker` Rejected by `cargo tauri build` + +**Cause:** CI runners set `CI=woodpecker` (string). Tauri CLI expects `true`/`false`. + +**Fix:** Prefix the build command: +```yaml +- run: CI=true cargo tauri build --target $TARGET +``` + +--- + +### Release Artifacts Not Uploaded + +**Cause 1:** `RELEASE_TOKEN` secret not set or expired. +```bash +# Recreate via admin CLI: +docker exec -u git gitea_app gitea admin user generate-access-token \ + --username sarman --token-name gitea-ci-token --raw \ + --scopes 'write:repository,read:user' +# Add the token as RELEASE_TOKEN in repo Settings > Actions > Secrets +``` + +**Cause 2:** Each build job uploads its own artifacts independently. All jobs require host network on the runner (see above). + --- ## Rust Compilation ### `MutexGuard` Not `Send` Across Await -**Error:** ``` error[E0277]: `MutexGuard<'_, Connection>` cannot be sent between threads safely ``` **Fix:** Release the mutex lock before any `.await` point: ```rust -// ✅ Correct let result = { let db = state.db.lock().map_err(|e| e.to_string())?; db.query_row(...)? @@ -76,21 +139,18 @@ async_fn().await?; ### Clippy Lints Fail in CI -Common lint fixes: +Common lint fixes required by `-D warnings` (Rust 1.88+): ```rust -// uninlined_format_args format!("{}", x) → format!("{x}") - -// range::contains x >= a && x < b → (a..b).contains(&x) - -// push_str single char s.push_str("a") → s.push('a') ``` Run locally: `cargo clippy --manifest-path src-tauri/Cargo.toml -- -D warnings` +Auto-fix: `cargo clippy --manifest-path src-tauri/Cargo.toml --fix --allow-dirty -- -D warnings` + --- ### `cargo tauri dev` Fails — Missing System Libraries @@ -101,6 +161,12 @@ sudo dnf install -y glib2-devel gtk3-devel webkit2gtk4.1-devel \ libsoup3-devel openssl-devel librsvg2-devel ``` +**Fix (Debian/Ubuntu):** +```bash +sudo apt-get install -y libwebkit2gtk-4.1-dev libssl-dev libgtk-3-dev \ + libayatana-appindicator3-dev librsvg2-dev patchelf pkg-config +``` + --- ## Database @@ -109,52 +175,41 @@ sudo dnf install -y glib2-devel gtk3-devel webkit2gtk4.1-devel \ **Symptom:** App fails to start with SQLCipher error. -**Check:** 1. `TFTSR_DB_KEY` env var is set 2. Key matches what was used when DB was created -3. File isn't corrupted (try `file tftsr.db` — should say `SQLite 3.x database`) - -**Warning:** Changing the key requires re-encrypting the database: -```bash -sqlite3 tftsr.db "ATTACH 'new.db' AS newdb KEY 'new-key'; \ - SELECT sqlcipher_export('newdb'); DETACH DATABASE newdb;" -``` +3. File isn't corrupted: `file tftsr.db` should say `SQLite 3.x database` --- ### Migration Fails to Run -Check which migrations have already been applied: +Check which migrations have been applied: ```sql SELECT name, applied_at FROM _migrations ORDER BY id; ``` -If a migration is partially applied, the DB may be in an inconsistent state. Restore from backup or recreate. - --- ## Frontend ### TypeScript Errors After Pulling -Run a fresh type check: ```bash npx tsc --noEmit ``` -Ensure `tauriCommands.ts` matches the Rust command signatures exactly (especially `IssueDetail` nesting). +Ensure `tauriCommands.ts` matches Rust command signatures exactly (especially `IssueDetail` nesting). --- ### `IssueDetail` Field Access Errors -The `get_issue` command returns a **nested** struct: +`get_issue()` returns a **nested** struct: ```typescript -// ✅ Correct +// Correct const title = detail.issue.title; -const severity = detail.issue.severity; -// ❌ Wrong — these fields don't exist at the top level +// Wrong — field doesn't exist at top level const title = detail.title; ``` @@ -162,26 +217,25 @@ const title = detail.title; ### Vitest Tests Fail -```bash -npm run test:run -``` - Common causes: - Mocked `invoke()` return type doesn't match updated command signature - `sessionStore` state not reset between tests (call `store.reset()` in `beforeEach`) --- -## Gogs +## Gitea -### Token Authentication +### API Token Authentication -The `sha1` field from the Gogs token create API **is** the bearer token — use it directly: ```bash -curl -H "Authorization: token " https://gogs.tftsr.com/api/v1/user +curl -H "Authorization: token " http://172.0.0.29:3000/api/v1/user ``` -Do not confuse with the `sha1` column in the `access_token` table, which stores `sha1(token)[:40]`. +Create tokens in Gitea Settings > Applications > Access Tokens, or via admin CLI: +```bash +docker exec -u git gitea_app gitea admin user generate-access-token \ + --username sarman --token-name mytoken --raw --scopes 'read:user,write:repository' +``` ### PostgreSQL Access @@ -189,4 +243,4 @@ Do not confuse with the `sha1` column in the `access_token` table, which stores docker exec gogs_postgres_db psql -U gogs -d gogsdb -c "SELECT id, lower_name, is_private FROM repository;" ``` -Database is named `gogsdb`, not `gogs`. +Database is named `gogsdb`. The PostgreSQL instance uses SCRAM-SHA-256 auth (MD5 also configured for the `gogs` user for compatibility with older clients).