docs: sync wiki with latest changes including LiteLLM + Bedrock guide

Shaun Arman 2026-03-31 12:25:56 -05:00
parent 10e1491638
commit 10b14a2ffe
6 changed files with 722 additions and 156 deletions

@ -19,7 +19,7 @@ pub trait Provider {
### 1. OpenAI-Compatible ### 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 | | 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` **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
--- ---

@ -4,10 +4,26 @@
| Component | URL | Notes | | Component | URL | Notes |
|-----------|-----|-------| |-----------|-----|-------|
| Gogs | `http://172.0.0.29:3000` / `https://gogs.tftsr.com` | Git server, version 0.14 | | 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` | v0.15.4 | | Woodpecker CI (direct) | `http://172.0.0.29:8084` | v2.x |
| Woodpecker CI (proxy) | `http://172.0.0.29:8085` | nginx with custom login page | | Woodpecker CI (proxy) | `http://172.0.0.29:8085` | nginx reverse proxy |
| PostgreSQL (Gogs DB) | Container: `gogs_postgres_db` | DB: `gogsdb`, User: `gogs` | | 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: Pipeline steps:
1. rust-fmt-check → cargo fmt --check 1. rust-fmt-check → cargo fmt --check
2. rust-clippy → cargo clippy -- -D warnings 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 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:** **Docker images used:**
- `rust:1.88-slim` — Rust steps (minimum for cookie_store + time + darling) - `rust:1.88-slim` — Rust steps (minimum for cookie_store + time + darling)
- `node:22-alpine` — Frontend steps - `node:22-alpine` — Frontend steps
**System dependencies installed in CI (Rust steps):** **Pipeline YAML format (Woodpecker 2.x — steps list format):**
```
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):**
```yaml ```yaml
clone: clone:
git: git:
image: woodpeckerci/plugin-git image: woodpeckerci/plugin-git
network_mode: gogs_default # requires repo_trusted=1 network_mode: gogs_default # requires repo_trusted=1
environment: 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: steps:
step-name: # KEY = step name (MAP, not list!) - name: step-name # LIST format (- name:)
image: rust:1.88-slim image: rust:1.88-slim
commands: commands:
- cargo test - 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*` **Triggers:** Git tags matching `v*`
``` ```
Pipeline steps: Jobs (run in parallel):
1. build-linux-amd64 → cargo tauri build (x86_64-unknown-linux-gnu) build-linux-amd64 → cargo tauri build (x86_64-unknown-linux-gnu)
2. build-linux-arm64 → cargo tauri build (aarch64-unknown-linux-gnu, cross-compile) → {.deb, .rpm, .AppImage} uploaded to Gitea release
3. upload-release → Create Gogs release + upload artifacts via API 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:** **Artifacts per platform:**
- Linux amd64: `.deb`, `.rpm`, `.AppImage` - 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 ```bash
# Create release # 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 Authorization: token $GOGS_TOKEN
# Upload artifact # 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 ## Webhook Configuration
**Hook ID:** 6 (in Gogs) **Woodpecker 2.x with Gitea OAuth2:**
**Events:** `create`, `push`, `pull_request`
**URL:** `http://172.0.0.29:8084/hook?access_token=<JWT>`
**JWT signing:** After migration, Woodpecker 2.x registers webhooks automatically when a repo is
- Algorithm: HS256 activated via the UI. No manual JWT-signed webhook setup required.
- Secret: `repo_hash` value from Woodpecker DB (`dK8zFWtAu67qfKd3Et6N8LptqTmedumJ`)
- Payload: `{"text":"sarman/tftsr-devops_investigation","type":"hook"}`
> ⚠️ 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 ```sql
-- User -- Gitea branch protection (via psql on gogs_postgres_db container)
SELECT user_token FROM users WHERE user_login='sarman'; -- Check protection
-- Should be: a6b3ee679fe9601e983f1a0c01493758d6033ef3 SELECT name, protected, require_pull_request FROM protect_branch WHERE repo_id=42;
-- Repo -- Temporarily disable for urgent fixes (restore immediately after!)
SELECT repo_active, repo_trusted, repo_config_path, repo_hash UPDATE protect_branch SET protected=false WHERE repo_id=42 AND name='master';
FROM repos WHERE repo_full_name='sarman/tftsr-devops_investigation'; -- ... push ...
-- repo_active=1, repo_trusted=1 UPDATE protect_branch SET protected=true, require_pull_request=true WHERE repo_id=42 AND name='master';
-- repo_config_path='.woodpecker/test.yml'
-- repo_hash='dK8zFWtAu67qfKd3Et6N8LptqTmedumJ'
``` ```
--- ---
## Known Issues & Fixes ## Known Issues & Fixes
### Webhook JWT Must Use `?access_token=` ### Step Containers Cannot Reach `gitea_app`
`token.ParseRequest()` in Woodpecker 0.15.4 does **not** read `?token=` URL params. Use `?access_token=<JWT>` instead. 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) ### `CI=woodpecker` Rejected by Tauri CLI
Hook JWT must be signed with the `repo_hash` value, not the user's hash. Woodpecker sets `CI=woodpecker`; `cargo tauri build` expects a boolean. Fix: prefix with
`CI=true cargo tauri build`.
### Directory-Based Config Not Supported ### Agent Stalls After Server Restart
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+. After restarting the Woodpecker server, the agent may enter a loop cleaning up orphaned
containers and stop picking up new builds. Fix:
### Step Containers Network Isolation ```bash
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`). docker rm -f $(docker ps -aq --filter 'name=0_')
docker volume rm $(docker volume ls -q | grep '0_')
### Empty Clone URL Bug docker restart woodpecker_agent
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 <sha1_from_create_response>
``` ```
### Gogs SPA Login Field Mismatch ### Windows DLL Export Ordinal Too Large
Gogs 0.14 SPA login form uses `login=` field; the Gogs backend reads `username=`. A custom login page is served by nginx at `/login`. `/usr/bin/x86_64-w64-mingw32-ld: error: export ordinal too large: 106290`
### Gogs OAuth2 Limitation Fix: `src-tauri/.cargo/config.toml`:
Gogs 0.14 has no OAuth2 provider support, blocking upgrade to Woodpecker 2.x. ```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 ```bash
docker exec gogs_postgres_db psql -U gogs -d gogsdb -c "SELECT id, lower_name FROM repository;" 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)

@ -90,7 +90,7 @@ npm run test:coverage
npx tsc --noEmit 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**.
--- ---

27
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. **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 ## Quick Navigation
| Topic | Description | | Topic | Description |
@ -10,9 +12,10 @@
| [Development Setup](wiki/Development-Setup) | Prerequisites, commands, environment | | [Development Setup](wiki/Development-Setup) | Prerequisites, commands, environment |
| [Database](wiki/Database) | Schema, migrations, encryption | | [Database](wiki/Database) | Schema, migrations, encryption |
| [AI Providers](wiki/AI-Providers) | Supported providers and configuration | | [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 | | [PII Detection](wiki/PII-Detection) | Patterns, redaction flow, security |
| [IPC Commands](wiki/IPC-Commands) | Full list of Tauri backend commands | | [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 | | [Security Model](wiki/Security-Model) | Encryption, audit trail, capabilities |
| [Integrations](wiki/Integrations) | Confluence, ServiceNow, Azure DevOps (v0.2) | | [Integrations](wiki/Integrations) | Confluence, ServiceNow, Azure DevOps (v0.2) |
| [Troubleshooting](wiki/Troubleshooting) | Known issues and fixes | | [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 - **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 - **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 - **SQLCipher AES-256** — All issue history encrypted at rest
- **RCA + Post-Mortem Generation** — Auto-populated Markdown templates, exportable as MD/PDF - **RCA + Post-Mortem Generation** — Auto-populated Markdown templates, exportable as MD/PDF
- **Ollama Management** — Hardware detection, model recommendations, in-app model management - **Ollama Management** — Hardware detection, model recommendations, in-app model management
- **Audit Trail** — Every external data send logged with SHA-256 hash - **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 - **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 ## Project Status
| Phase | Status | | Phase | Status |
|-------|--------| |-------|--------|
| Phases 18 (Core) | ✅ Complete | | Phases 18 (Core application) | ✅ Complete |
| Phase 9 (History/Search FTS) | 🔄 Partially integrated | | Phase 9 (History/Search) | 🔲 Pending |
| Phase 10 (Integrations) | 🕐 v0.2 stubs only | | Phase 10 (Integrations) | 🕐 v0.2 stubs only |
| Phase 11 (CLI) | 🕐 Planned | | Phase 11 (CI/CD) | ✅ Complete — Gitea Actions fully operational |
| Phase 12 (Release packaging) | 🔄 Linux done; macOS/Windows pending | | Phase 12 (Release packaging) | ✅ linux/amd64 · linux/arm64 (native) · windows/amd64 |
## Tech Stack ## Tech Stack
@ -49,5 +60,5 @@
| Database | rusqlite + SQLCipher (AES-256) | | Database | rusqlite + SQLCipher (AES-256) |
| Secret storage | tauri-plugin-stronghold | | Secret storage | tauri-plugin-stronghold |
| State | Zustand | | State | Zustand |
| Testing | Vitest (frontend) + `#[cfg(test)]` (Rust) | | Testing | Vitest (13 frontend) + `#[cfg(test)]` (64 Rust tests) |
| CI/CD | Woodpecker CI v0.15.4 + Gogs | | CI/CD | Gitea Actions (act_runner v0.3.1) + Gitea |

396
LiteLLM-Bedrock-Setup.md Normal file

@ -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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.litellm.proxy</string>
<key>ProgramArguments</key>
<array>
<string>/opt/homebrew/bin/litellm</string>
<string>--config</string>
<string>/Users/${USER}/.litellm/config.yaml</string>
<string>--port</string>
<string>8000</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/${USER}/.litellm/litellm.log</string>
<key>StandardErrorPath</key>
<string>/Users/${USER}/.litellm/litellm-error.log</string>
</dict>
</plist>
```
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)

@ -1,70 +1,133 @@
# Troubleshooting # 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. **Check:**
1. Verify the workflow file exists in `.gitea/workflows/` on the pushed branch
**Fix:** Webhook URL must use `?access_token=<JWT>` (not `?token=`). 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`
http://172.0.0.29:8084/hook?access_token=<JWT>
```
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)
```
--- ---
### 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`: **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:
```bash
docker exec woodpecker_db sqlite3 /data/woodpecker.sqlite \
"UPDATE repos SET repo_trusted=1 WHERE repo_full_name='sarman/tftsr-devops_investigation';"
```
---
### 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 ```yaml
clone: # /docker_mounts/gitea/runner/amd64/config.yaml
git: container:
environment: network: "host"
- CI_REPO_CLONE_URL=http://gogs_app:3000/sarman/tftsr-devops_investigation.git
``` ```
```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 ## Rust Compilation
### `MutexGuard` Not `Send` Across Await ### `MutexGuard` Not `Send` Across Await
**Error:**
``` ```
error[E0277]: `MutexGuard<'_, Connection>` cannot be sent between threads safely error[E0277]: `MutexGuard<'_, Connection>` cannot be sent between threads safely
``` ```
**Fix:** Release the mutex lock before any `.await` point: **Fix:** Release the mutex lock before any `.await` point:
```rust ```rust
// ✅ Correct
let result = { let result = {
let db = state.db.lock().map_err(|e| e.to_string())?; let db = state.db.lock().map_err(|e| e.to_string())?;
db.query_row(...)? db.query_row(...)?
@ -76,21 +139,18 @@ async_fn().await?;
### Clippy Lints Fail in CI ### Clippy Lints Fail in CI
Common lint fixes: Common lint fixes required by `-D warnings` (Rust 1.88+):
```rust ```rust
// uninlined_format_args
format!("{}", x) → format!("{x}") format!("{}", x) → format!("{x}")
// range::contains
x >= a && x < b (a..b).contains(&x) x >= a && x < b (a..b).contains(&x)
// push_str single char
s.push_str("a") → s.push('a') s.push_str("a") → s.push('a')
``` ```
Run locally: `cargo clippy --manifest-path src-tauri/Cargo.toml -- -D warnings` 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 ### `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 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 ## Database
@ -109,52 +175,41 @@ sudo dnf install -y glib2-devel gtk3-devel webkit2gtk4.1-devel \
**Symptom:** App fails to start with SQLCipher error. **Symptom:** App fails to start with SQLCipher error.
**Check:**
1. `TFTSR_DB_KEY` env var is set 1. `TFTSR_DB_KEY` env var is set
2. Key matches what was used when DB was created 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`) 3. File isn't corrupted: `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;"
```
--- ---
### Migration Fails to Run ### Migration Fails to Run
Check which migrations have already been applied: Check which migrations have been applied:
```sql ```sql
SELECT name, applied_at FROM _migrations ORDER BY id; 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 ## Frontend
### TypeScript Errors After Pulling ### TypeScript Errors After Pulling
Run a fresh type check:
```bash ```bash
npx tsc --noEmit 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 ### `IssueDetail` Field Access Errors
The `get_issue` command returns a **nested** struct: `get_issue()` returns a **nested** struct:
```typescript ```typescript
// Correct // Correct
const title = detail.issue.title; 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; const title = detail.title;
``` ```
@ -162,26 +217,25 @@ const title = detail.title;
### Vitest Tests Fail ### Vitest Tests Fail
```bash
npm run test:run
```
Common causes: Common causes:
- Mocked `invoke()` return type doesn't match updated command signature - Mocked `invoke()` return type doesn't match updated command signature
- `sessionStore` state not reset between tests (call `store.reset()` in `beforeEach`) - `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 ```bash
curl -H "Authorization: token <sha1_value>" https://gogs.tftsr.com/api/v1/user curl -H "Authorization: token <token_value>" 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 ### 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;" 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).