Dev environment — Rymflux v2
Dev environment — Rymflux v2
Status
Draft v1 — 2026-07-31
Purpose
This document is the setup and day-to-day reference for developers working on the Rymflux v2 desktop app. It covers the repository layout (the Cargo workspace plus the SvelteKit frontend), the toolchain and version floor, one-time bootstrap, the everyday run / lint / test / build commands, and the environment variables and on-disk paths the app reads and writes.
It is how you get code running. It is deliberately not about how the app is engineered — runtime model, layer rules, the IPC bridge, and the state layer are owned by frontend-architecture.md; the backend services are described in container-diagram.md; user data and logging conventions are in cross-cutting-concerns.md.
Related sources
- frontend-architecture.md — §1.1 (build config), §2 (frontend tree), §13 (testing), §16.4 (type-parity tooling)
- container-diagram.md — the logical backend modules this workspace maps to crates
- cross-cutting-concerns.md — logging, error handling, the
~/.rymflux/data layout - first-time-setup.md — the hydration flow the app follows on first launch
- strategy.md — what
pnpm test/cargo testactually cover
Repository layout
rymflux/
├── Cargo.toml # virtual workspace manifest (edition 2024, resolver "3")
├── Cargo.lock # one lockfile for the whole workspace
├── crates/ # Rust workspace members (container-diagram services)
│ ├── contracts/ # serde types mirroring api-specs/data-models.md (snake_case)
│ ├── plugin-runtime/ # JSON-RPC plugin supervision (ADR-001, ADR-011)
│ ├── search-manager/ # parallel search dispatch + merge (ADR-007)
│ ├── playback-engine/ # phonic (symphonia + rubato + cpal) (ADR-004)
│ └── library-service/ # rusqlite; favorites, playlists, history, resolution (ADR-002)
├── src-tauri/ # Tauri app crate (the single binary; wires the crates)
│ ├── src/ # command/event handlers, managed state, migrations runner
│ ├── migrations/ # SQL migrations (001_*.sql …)
│ └── tauri.conf.json # CSP, window, build.frontendDist / devUrl
├── components.json # shadcn-svelte contract (ui alias → src/lib/ui)
├── vitest.config.ts # Vitest node + browser projects (testing/strategy.md)
├── svelte.config.js # adapter-static SPA + aliases
├── vite.config.ts # sveltekit(), strictPort
├── package.json
├── tsconfig.json
├── eslint.config.js
├── .prettierrc
├── README.md
└── src/ # SvelteKit frontend (see frontend-architecture.md §2)
Docs-as-Code: this doc and the rest of the docs scaffold live in the sibling
rymflux-v2/ repository, not in the code repo shown above.
Cargo workspace
The backend is a single Rust process (container-diagram.md). The workspace
expresses that as one binary crate (src-tauri) that depends on a set of
library crates under crates/. This preserves the documented single-process
design while keeping each service unit-testable in isolation — the crates are
logical modules, not separate deployable units.
- The root
Cargo.tomlis a virtual manifest (no[package]): it declares only[workspace],[workspace.dependencies], andresolver = "3". edition = "2024"(requires Rust 1.85+; see Toolchain).resolver = "3"is the edition 2024 default and is spelled out explicitly.- Shared dependencies are pinned once in
[workspace.dependencies]and referenced from each member crate so versions never drift. The full inventory is the dependency manifest below. - There is one
Cargo.lockand one sharedtarget/at the workspace root; thecrates/layout is flat (single level) — no nested workspace crates. src-tauriis the only[[bin]]. Library crates declare no binaries.
Dependency manifest
Every crate in [workspace.dependencies], its role, and where the requirement
comes from. Versions are the v1 reference pins — proven by the previous
incarnation’s Cargo.toml — and are provenance only: re-verify all of them
when the workspace is first scaffolded; nothing below is asserted as current.
| Crate | Type | Purpose | Primary consumer | Source |
|---|---|---|---|---|
tauri (^2) |
runtime | App shell, IPC, events, windowing | src-tauri |
frontend-architecture.md §1.1 |
serde (derive) |
runtime | Strict-type JSON-RPC parsing + data-models.md mirror |
contracts, all services |
data-models.md, cross-cutting-concerns.md |
serde_json (1.0) |
runtime | JSON-RPC 2.0 wire format | plugin-runtime, search-manager |
v1 provenance |
toml |
runtime | config.toml + plugin.toml manifest parsing |
plugin-runtime |
cross-cutting-concerns.md, ADR-011 |
tokio |
runtime | Async runtime — io, select!, timeouts, sync::mpsc |
all services | container-diagram.md |
uuid |
runtime | UUIDv4 content IDs | library-service, contracts |
ADR-003 |
phonic (0.16, cpal-output, default-features off) |
runtime | Audio engine — wraps symphonia + rubato + cpal | playback-engine |
container-diagram.md, ADR-004 |
rusqlite (0.39, bundled) |
runtime | Library storage; migrations via pragma user_version |
library-service |
ADR-002 |
reqwest (0.13, blocking + rustls, default-features off) |
runtime | Fetch stream URLs (URL-only streaming) | playback-engine |
ADR-006 |
tracing |
runtime | Structured logging | all services | cross-cutting-concerns.md |
tracing-subscriber (0.3, env-filter, registry) |
runtime | Log output to app.log/stdout; implements RUST_LOG |
src-tauri |
cross-cutting-concerns.md; v1 provenance |
thiserror (2.0) |
runtime | Typed three-tier error model | all services | cross-cutting-concerns.md; v1 provenance |
chrono (0.4, serde) |
runtime | ISO 8601 timestamps, added_at |
library-service, logging |
cross-cutting-concerns.md; v1 provenance |
image (0.25) |
runtime | ADR-016 dominant color — decode + 1×1 resize + oklch average | library-service |
ADR-016; v1 provenance |
tempfile (3.27) |
dev | Temp database for Rust integration tests | src-tauri tests |
testing/strategy.md; v1 provenance |
phonic wav-output |
dev | Audio test fixtures (render to wav for assertions) | playback-engine tests |
v1 provenance |
Superseded v1 crates — do not re-add: blake3 and sha2 (v1 content
addressing / cache keys — superseded by UUIDv4, ADR-003) and
crossbeam-channel (superseded by tokio::sync::mpsc,
container-diagram.md). v1’s reqwest
download-to-cache flow (archive.org / LibriVox) is dead: in v2, reqwest
serves URL streaming only (ADR-006), never a local cache.
Tooling (cargo subcommands, not dependencies): cargo llvm-cov (coverage),
cargo-audit (security scan) — see testing/strategy.md.
Toolchain
| Tool | Version floor | Notes |
|---|---|---|
| Rust | stable 1.85+ | edition = "2024" needs 1.85. Backend MSRV floor is 1.77 (Tauri 2.9.x); the repo uses the latest stable — no pinning below stable unless CI requires it. |
| rustup components | rustfmt, clippy, rust-analyzer |
Installed once; required for the fmt/lint gates. |
| Node | 20 LTS+ (22/24 LTS fine) | Tauri v2 requirement. |
| pnpm | current | Package manager (ui-design-system.md pins pnpm add); enable via corepack or install directly. |
| Tauri CLI | v2 (^2) |
Use @tauri-apps/cli as a dev dependency and run pnpm tauri … so the CLI version matches the app’s Tauri version. |
| SvelteKit / Svelte | SvelteKit 2.x, Svelte 5 | SPA mode via adapter-static (frontend-architecture.md §1.1). |
| Tailwind | v4 | @tailwindcss/vite, tw-animate-css; config lives in CSS (@theme, @custom-variant dark), no tailwind.config. |
| shadcn-svelte | latest (Tailwind v4 support) | Style: Rhea (compact Luma). Init sets components.json aliases so components land in src/lib/ui/. |
System dependencies (one-time, per platform)
Linux (apt-based):
sudo apt install libwebkit2gtk-4.1-dev build-essential curl wget file \
libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev
macOS: xcode-select --install only — the webview (WKWebView) and windowing
toolkit are part of the OS.
Windows: Visual Studio Build Tools (Desktop development with C++) and the WebView2 runtime (preinstalled on current Windows 10/11).
Bootstrap
# 1. Rust toolchain + components
rustup toolchain install stable && rustup component add rustfmt clippy rust-analyzer
# 2. Node + pnpm (once)
corepack enable # or: npm install -g pnpm
# 3. Frontend dependencies
pnpm install
# 4. shadcn-svelte (Rhea), with the ui alias pointing at src/lib/ui
pnpm dlx shadcn-svelte@latest init
# → confirms components.json aliases: ui → src/lib/ui
# 5. First Rust compile (slow — fetches and builds the dependency tree)
cargo build --workspace
The first cargo build is the only long step; after it the incremental build
and pnpm tauri dev are fast.
Day-to-day commands
| Command | Purpose |
|---|---|
pnpm tauri dev |
Full app: builds the workspace, boots the Rust process, serves the SvelteKit app with HMR on the Vite dev server (port 1420, strictPort). |
pnpm dev |
Frontend-only Vite dev server. No IPC backend — usable for markup/state work only where the ipc/ bridge is mocked (see strategy.md). |
pnpm build |
Production SvelteKit build (SPA bundle into build/). |
pnpm tauri build |
Release bundle (frontend build + optimized Rust + platform installer). |
pnpm check |
svelte-check — type-checks Svelte components, routes, and the .svelte.ts state layer. Run before pushing. |
pnpm lint / pnpm format |
ESLint / Prettier (configs at repo root). |
pnpm test |
Frontend tests: Vitest node project (state layer) + browser project (component/integration, chromium) — see strategy.md. |
cargo test --workspace |
All Rust unit + integration tests across crates/ and src-tauri. |
cargo clippy --workspace --all-targets |
Rust lint gate (deny-warnings in CI). |
cargo fmt --check |
Rust format gate. |
Configuration & environment
| Setting | Where | Notes |
|---|---|---|
| CSP | src-tauri/tauri.conf.json → app.security.csp |
Set once; allows self and ipc:. No separate web CSP (frontend-architecture.md §1.1). |
app.withGlobalTauri |
src-tauri/tauri.conf.json |
false; the frontend imports @tauri-apps/api explicitly through the ipc/ bridge. |
frontendDist / devUrl |
src-tauri/tauri.conf.json |
../build and the Vite dev server respectively. |
RUST_LOG |
shell | RUST_LOG=rymflux=debug pnpm tauri dev for full backend debug logs on stdout (cross-cutting-concerns.md §Logging). |
| App data | ~/.rymflux/ |
config.toml, data/library.db, logs/app.log + logs/plugins/<id>.log, plugins/, crash-reports/. macOS-first v1; XDG directories considered for later (cross-cutting-concerns.md). |
Debugging notes
- Backend:
RUST_LOG=rymflux=debugis the primary diagnostic; every JSON-RPC message, Tauri command invocation, and SQL query is logged atdebug(cross-cutting-concerns.md). - Frontend: the webview console is supplementary. Logs carry the same
contextual fields (
content_id,plugin_id,query) as the backend sologs/app.logand the console tell one story. - Plugin problems: per-plugin stderr is captured to
~/.rymflux/logs/plugins/<plugin_id>.log(ADR-001). - Crashes:
std::panic::set_hookwrites a report to~/.rymflux/crash-reports/and shows a non-blocking dialog. - First-launch hydration follows
first-time-setup.md— if the UI hangs on the loading gate, theplatform_readyevent has not arrived; check the backend log first. pnpm tauri devon a stale browser-mode cache can surface as weird DOM failures in tests — see strategy.md for the chromium install /flushSync()notes.
Platform notes
- Linux: requires
webkit2gtk-4.1(not the older 4.0); a missing system dep shows up atcargo build/ firstpnpm tauri dev. - macOS: no extra webview deps; code-signing/notarization are out of scope
for local dev. E2E later uses the
tauri-wdautomation server (strategy.md). - Windows: WebView2 runtime is preinstalled on current builds; the E2E
WebDriver path (
tauri-driver) is the same as Linux.