tftsr-devops_investigation/docs/wiki/Development-Setup.md
Shaun Arman 281e676ad1 fix(security): harden secret handling and audit integrity
Remove high-risk defaults and tighten data handling across auth, storage, IPC, provider calls, and capabilities so sensitive data is better protected by default. Also update README/wiki security guidance and add targeted tests for the new hardening behaviors.

Made-with: Cursor
2026-04-04 23:37:05 -05:00

3.7 KiB

Development Setup

Prerequisites

System (Linux/Fedora)

sudo dnf install -y glib2-devel gtk3-devel webkit2gtk4.1-devel \
  libsoup3-devel openssl-devel librsvg2-devel

Rust

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env

Minimum required version: Rust 1.88 (needed by cookie_store, time, darling).

Node.js

Node v22 required. Install via nvm or system package manager.

Project Dependencies

npm install --legacy-peer-deps

Environment Variables

Variable Default Purpose
TFTSR_DATA_DIR Platform data dir Override DB location
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:

  • Linux: ~/.local/share/tftsr/
  • macOS: ~/Library/Application Support/tftsr/
  • Windows: %APPDATA%\tftsr\

Development Commands

Start Full Dev Environment

source ~/.cargo/env
cargo tauri dev

Hot reload: Vite (frontend at localhost:1420) + Tauri (Rust recompiles on save).

Frontend Only

npm run dev
# → http://localhost:1420

Testing

# Rust unit tests
cargo test --manifest-path src-tauri/Cargo.toml

# Run a single test module
cargo test --manifest-path src-tauri/Cargo.toml pii::detector

# Run a single test by name
cargo test --manifest-path src-tauri/Cargo.toml test_detect_ipv4

# Frontend tests (single run)
npm run test:run

# Frontend tests (watch mode)
npm run test

# Frontend coverage report
npm run test:coverage

# TypeScript type check
npx tsc --noEmit

Current test status: 13/13 frontend tests passing, 64/64 Rust tests passing.


Linting & Formatting

# Rust format check
cargo fmt --manifest-path src-tauri/Cargo.toml --check

# Auto-format
cargo fmt --manifest-path src-tauri/Cargo.toml

# Rust lints (all warnings as errors)
cargo clippy --manifest-path src-tauri/Cargo.toml -- -D warnings

# Quick Rust type check (no linking)
cargo check --manifest-path src-tauri/Cargo.toml

Production Build

cargo tauri build
# → src-tauri/target/release/bundle/
# Outputs: .deb, .rpm, .AppImage (Linux)

Release builds enforce secure key configuration. Set both TFTSR_DB_KEY and TFTSR_ENCRYPTION_KEY before building.


Rust Design Patterns

Mutex Release Before Await

MutexGuard is not Send. Always release the lock before any .await:

// ✅ CORRECT — release lock before await
let value = {
    let db = state.db.lock().map_err(|e| e.to_string())?;
    db.query_row(...)?
};   // ← lock released here
some_async_call().await?;

// ❌ WRONG — compile error: MutexGuard not Send across await
let db = state.db.lock()?;
let result = some_async_call().await?;  // ERROR

Database Queries (Lifetime Issue)

Use conn.prepare().and_then(...) pattern:

// ✅ CORRECT
let rows = conn.prepare("SELECT ...")
    .and_then(|mut stmt| stmt.query_map(params![], |row| { ... })?.collect())?;

// ❌ causes lifetime issues in async context
let mut stmt = conn.prepare("SELECT ...")?;
let rows = stmt.query_map(...)?;

Command Handler Pattern

#[tauri::command]
pub async fn my_command(
    param: String,
    state: State<'_, AppState>,
) -> Result<ResponseType, String> {
    let result = {
        let db = state.db.lock().map_err(|e| e.to_string())?;
        db.query_row("SELECT ...", params![param], |row| { ... })
            .map_err(|e| e.to_string())?
    };
    Ok(result)
}