Supply chain defense for developers. System tray app + package manager guard. See README.md for project background and motivation.
- README.md — what Ripley is and why it exists
- ARCHITECTURE.md — system design, component specs, tech stack, threat model
- STACK_DECISION.md — Phase 6 UI stack: Tauri 2 + React 19 + shadcn/Base UI + Tailwind v4 + Zustand + TanStack Query + tauri-specta. Canonical versions, tooling, software patterns, repo layout
- DESIGN.md — design system: colors, typography, spacing, component styling, do's/don'ts
- DESIGN_ISSUES.md — Phase 6 view-migration priority list (was iced audit; same priorities)
- UI.md — view wireframes, interaction specs, tray icon, dashboard, dialogs
- UX_DESIGN.md — first-principles UX/DX: workflow→component mapping, keyboard model, DX patterns for M27
- GOAL.md — Phase 6 autonomous-execution operating manual for
/goal-driven runs (turn rituals, halt token grammar, condition block) - DECISIONS.md — rationale behind each technical choice, competitive landscape
- ROADMAP.md — phased execution plan with milestones and verification criteria
- WORKFLOW.md — user workflow streams: setup, scan, monitoring, interception, fix, forensics, CI, audit, harden, exposure, uninstall
- SETTINGS.md — complete reference for all settings, config layers, env vars, defaults
Available today (Phase 1-5 shipped, Rust-only workspace):
cargo build --workspace
cargo test --workspace
cargo clippy --workspace
cargo fmt --all -- --check
cargo deny check # audit own supply chain
cargo run -p ripley-guard -- scan --format json tests/fixtures/
Becomes available with M24.1 (Phase 6 scaffolding — justfile, package.json,
pnpm-workspace.yaml, .mise.toml, lefthook.yml are created in that milestone):
just dev # Tauri dev (pnpm + vite + cargo, hot reload)
just build # cargo build --workspace + pnpm tauri build
just test # cargo test --workspace + pnpm -F desktop test
just check # clippy + fmt + tsc + eslint + cargo deny + pnpm audit
Frontend commands (M24+, run from repo root; not present on main until M24.1 lands):
pnpm install # install JS deps (pnpm only — never npm/yarn)
pnpm -F desktop tauri dev # launch desktop in dev mode
pnpm -F desktop tauri build # bundle .dmg / .msi / .deb / .rpm / AppImage
pnpm -F desktop test # Vitest unit tests
pnpm -F desktop test:e2e # WebdriverIO (Linux + Windows only)
pnpm audit # JS supply chain audit
cargo run -p ripley-guard -- --help
cargo run -p ripley-guard -- scan .
cargo run -p ripley-guard -- scan --deep .
cargo run -p ripley-guard -- scan --format json .
cargo run -p ripley-guard -- guard status
Phase 6 — UI Rewrite (Tauri 2). Phases 1-5 shipped (M1-M23, 510+ tests). Current milestone: M24 — Tauri scaffold & shell. See STACK_DECISION.md for the locked stack/tooling/patterns and PLAN.md M24-M28 for the task breakdown.
Phase 5 delivered: SARIF output, CI/CD integration, sandboxed script execution,
behavioral analysis, community rule sharing with three-tier loading. The
crates/ripley-app iced dashboard is the migration target; it is retired in M28.
Phase 6 is driven by Claude Code's built-in /goal command. The full
operating manual is GOAL.md. If you are running inside a /goal loop:
- Read
GOAL.mdfirst thing every turn — it contains the turn-start ritual, turn-end ritual, halt token grammar, and source-of-truth precedence. - The condition (≤4000 chars) lives at the bottom of
GOAL.md. The human pastes only that block into/goal. Everything else is read by Claude per-turn from the file. - Loop terminates only on explicit tokens — the success token
Phase 6 complete. Release v0.6.0 cut., a[HALT-AND-WAIT: <code>]halt token from the enumerated list in GOAL.md §5b, or[HALT-MAX-TURNS]. Partial progress does NOT terminate the loop; the evaluator just re-invokes. - Wait for or kill background shells before ending a turn —
/goalpauses if the evaluator fires while background work is still running. - Single state file:
apps/desktop/.ripley-agent/turns(gitignored, just the turn counter for the 500-turn cap). All other state is reconstructed from git + PLAN.md[x]checkboxes +gh pr list.
- Error handling:
thiserrorinripley-core(library),anyhowinripley-guard(binary) - Async runtime:
tokio(multi-threaded,features = ["full"]) - HTTP client:
reqwestwithrustls(no OpenSSL/C dependency) - Serialization:
serde+serde_jsonwith derive macros;tomlfor config files - CLI parsing:
clapv4 with derive macros - CLI output: support
--format json|table, exit codes: 0=clean, 1=findings, 2=error - Logging:
tracing+tracing-subscriberwithenv-filter;tracing-appenderfor file logging in daemon mode - Advisory cache:
redbv4 (pure Rust embedded KV store) — advisory data only - Configuration:
config.tomlin platform config dir — human-editable, layered (user → project → env → CLI). Overlay pattern withOption<T>fields for merge. - Guard log: append-only JSONL at
{data_dir}/guard.jsonl - Lockfile index: in-memory only, rebuilt on startup and filesystem events — never persist derived data
- Platform directories:
directoriescrate — never hardcode~/.ripley/ - Version matching:
semvercrate - Filesystem watching:
notifycrate v8 - Pattern matching:
regexcrate (cache compiled regexes) - UI framework (Phase 6, in progress): Tauri 2.11 + React 19.2 + shadcn/ui (Base UI primitive) + Tailwind v4. The iced-based
crates/ripley-app(tray-icon+muda) is the migration target; retired in M28 - Frontend package manager: pnpm only — npm and yarn are forbidden (phantom-dep risk for a supply-chain tool)
- Frontend build tool: Vite 8 (Rolldown + Oxc; no Babel)
- Frontend language: TypeScript 6 (TS 7 Go-native port still beta — defer)
- Frontend styling: Tailwind v4 with
@themeblock in CSS (notailwind.config.ts); DESIGN.md tokens inapps/desktop/src/styles/theme.css - Frontend components:
@base-ui/react1.x via shadcn CLI v4 (pnpm dlx shadcn@latest init+--base base-ui, stylebase-vega). Copy-into-repo atapps/desktop/src/components/ui/ - Frontend state: TanStack Query v5 for IPC ("server state") + Zustand 5 for UI state (slice pattern at >2 slices). Do not mirror server state into Zustand
- Type-safe IPC:
tauri-spectav2 emits TS bindings from#[tauri::command]+specta::Typeannotations intoapps/desktop/src/lib/bindings.ts. Generated bindings are committed, not gitignored (same principle asCargo.lock). No hand-writteninvoke<T>("cmd")calls - Frontend linting: ESLint 9 flat config (
eslint.config.js) +typescript-eslintv8 + React + jsx-a11y plugins. Biome rejected — missingreact-hooks/exhaustive-depsand security plugin breadth - Frontend formatting: Prettier 3 paired via
eslint-config-prettier - Frontend unit tests: Vitest 2.x +
@testing-library/react16+ + jsdom +@vitest/coverage-v8 - Frontend e2e tests: WebdriverIO 9 +
tauri-driver. Linux + Windows CI only; macOS WKWebView gap accepted and covered by Vitest + Peekaboo screenshots - Browser regression tests: Playwright 1.x against Vite-served React (NOT the Tauri shell). Complements WebdriverIO — Playwright covers per-view a11y (
@axe-core/playwright), Lighthouse scores (playwright-lighthouse: a11y ≥95, perf ≥90, best-practices ≥95), visual diffs, DESIGN.md token conformance viagetComputedStyle, perf budgets (mount-to-interactive <50ms warm), and bundle-size ceilings. Runs on all three OS runners. Specs live inapps/desktop/tests/browser/. Invoked viajust test:browser - Interactive verification (agent-only): Chrome DevTools MCP — agent-driven Chromium for PR self-review of any view touching DESIGN.md tokens,
KEYMAP, or IPC bindings. Canonical loop:new_page→navigate_page→take_snapshot→take_screenshot→list_console_messages→list_network_requests→lighthouse_audit→close_page. Not a runtime dep, not part of CI; documented atapps/desktop/docs/mcp-verification.md - Git hooks: Lefthook 1.x (
lefthook.ymlat root). Droplint-staged— Lefthook handles staged filtering natively - Versioning:
release-plzfor Rust + Conventional Commits as single source of truth. Changesets only ifpackages/*ever publishes JS - Task runner:
just(justfileat root). All top-level commands proxy through it - Tool version pinning:
mise(.mise.tomlat root) pins node, pnpm, just, lefthook, tauri-cli - JS supply chain audit:
pnpm auditin CI; new JS deps reviewed with the same scrutiny as new Rust deps - Snapshot testing:
instacrate for analyzer output, CLI output, prompt format (Rust side); Vitest snapshots for React component output - Tests: unit tests in
#[cfg(test)] mod testsblocks (Rust),*.test.ts(x)colocated (TS), integration tests intests/ - No
unwrap()orexpect()inripley-core— always returnResult - No
unsafeunless there is a documented, measured reason - Detection rules: TOML files compiled in via
include_str!(base) + loaded at runtime from{config_dir}/rules/(user) - Test fixtures live in
tests/fixtures/at the workspace root - File writes to config/RC files must be atomic (write temp, then rename)
- Phase 6 in progress (UI rewrite to Tauri 2). Do not implement Phase 7+ features (plugin system, hosted dashboard, deep tracing). Use trait/enum extension points where the design needs them for later phases.
- New deps: Evaluate carefully. Do not add crates or JS packages without a clear reason. JS deps cost the same audit attention as Rust deps for a supply-chain tool.
- Follow existing patterns. Audit checks use collect/evaluate pattern:
collect_*()runs system commands (platform-specific viacfg(target_os)),evaluate_*(output)is pure and testable. Traffic-light output usesTrafficLightenum across audit and harden. New CLI commands follow existingscan.rsshape (cmd_*,--format, exit codes). Prompt generation extendsprompt.rs. Harness reusesharness.rs. - Cross-platform. Use
cfg(target_os)for platform-specific system checks. Never hardcode macOS commands in shared code. Useplatform.rshelpers for paths, shells. - Test as you go. Every public function in
ripley-coreshould have at least one unit test. Audit checks test viaevaluate_*with mock command output. Useinstasnapshot tests for all command outputs. - Keep dependencies minimal. Do not add new crates without a clear reason.
- Own supply chain. Run
cargo deny checkas part of verification. A supply chain security tool that doesn't audit its own dependencies has no credibility.