update CLAUDE.md, README.md and justfile

This commit is contained in:
2026-05-15 11:05:45 +02:00
parent 2bbd2ac869
commit 2e3d6a9abb
8 changed files with 56 additions and 228 deletions
-3
View File
@@ -1,3 +0,0 @@
- always check with `cargo clippy` and fix the issues
- always do a final `cargo +nightly fmt` after you're done with all changes
- use `cargo tauri build --debug --no-bundle` if you need to build the frontend (a.k.a. `lanspread-tauri-deno-ts` crate)
Symlink
+1
View File
@@ -0,0 +1 @@
CLAUDE.md
+22 -181
View File
@@ -1,191 +1,32 @@
# CLAUDE.md # lanspread
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. Peer-to-peer game library sharing for LAN parties. Peers discover each other on the local network via mDNS, exchange library metadata over QUIC, and let users browse and download games from each other. Ships as a Tauri desktop app.
## Project Overview ## Workspace layout
**LanSpread2** is a peer-to-peer (P2P) game distribution system with a Tauri desktop application frontend. It enables users to share games over local networks using mDNS discovery and QUIC protocol for peer communication. Cargo workspace under `crates/`:
## Common Development Commands - `lanspread-peer` — core peer: networking, library, downloads, services. Start here for behavior changes. See `crates/lanspread-peer/ARCHITECTURE.md`.
- `lanspread-proto` — wire protocol types shared across peers.
- `lanspread-mdns` — mDNS-SD discovery wrapper.
- `lanspread-db` — database/schema types (sqlx + sqlite).
- `lanspread-compat` — compatibility/migration glue between db and other crates.
- `lanspread-utils` — small shared helpers.
- `lanspread-tauri-deno-ts/` — frontend (Vite + Deno + TS in `src/`) and Tauri shell (`src-tauri/`). This is the GUI client.
### Build Top-level `Cargo.toml` pins workspace dependency versions; per-crate `Cargo.toml`s set lints (pedantic clippy, `unsafe_code = forbid` on most).
```bash ## Commands (justfile)
# Development (opens UI, needs user interaction)
cargo tauri dev
# Debug build for very quick testing (without bundle) Never use normal cargo ... commands, use the just ... commands instead.
cargo tauri build --debug --no-bundle
# Release build for testing (without bundling) - `just run` — run the GUI in dev mode.
cargo tauri build --no-bundle - `just build` — build the GUI without bundling (also dev mode).
- `just fmt` — format the workspace.
- `just clippy` — lint the workspace.
- `just fix` — auto-apply cargo/clippy fixes, then format.
- `just clean` — wipe the build cache.
# Production (with bundling) ## General info
cargo tauri build -- --profile release-lto
```
### Build Profiles See `README.md`.
- `release`: Default release profile with debug info, assertions enabled, overflow checks on, for testing
- `release-lto`: Optimized for distribution with LTO enabled, debug symbols stripped, smaller binary, only for production
### Code Quality
```bash
# Lint all code
cargo clippy
# Format code (nightly Rust)
cargo +nightly fmt
```
### Testing
```bash
# Run all tests
cargo test --workspace
# Run tests for a specific crate
cargo test -p lanspread-peer
# Run a specific test
cargo test -p lanspread-peer test_name
# Run tests with output
cargo test -- --nocapture
```
### Dependencies
```bash
# Update frontend dependencies (Deno)
deno outdated --update --latest
```
### Wayland Users
```bash
# Set this environment variable if running on Wayland
WEBKIT_DISABLE_DMABUF_RENDERER=1
```
## Architecture
LanSpread follows a layered, modular architecture with clear separation of concerns:
### Crate Structure
The project is organized as a Cargo workspace with 7 crates:
**Foundation Layer:**
- **lanspread-utils**: Utility macros and helpers used across all crates
- **lanspread-db**: Core data structures (`Game`, `GameFileDescription`, game metadata)
**Protocol & Communication:**
- **lanspread-proto**: P2P communication protocol definitions
- Message types: `Request` (Ping, ListGames, GetGame, etc.), `Response`, `Message` trait
- Serialization: JSON with `serde`
**Network Discovery & Compatibility:**
- **lanspread-mdns**: mDNS service discovery and advertisement
- Advertises and discovers "_lanspread._udp.local." services on LAN
- Uses `mdns-sd` crate
- **lanspread-compat**: Compatibility layer for legacy ETI game database format
- Reads legacy `game.db` SQLite databases
- Converts `EtiGame` structs to modern `Game` struct
**Core P2P Engine:**
- **lanspread-peer**: Central orchestration for all P2P functionality
- Entry point: `start_peer()` in `lib.rs`
- Communicates via unbounded channels (`PeerEvent`, `PeerCommand`)
- Key modules:
- `services.rs`: Background tasks (server, discovery, ping, local game monitor)
- `handlers.rs`: Command handlers for UI requests
- `network.rs`: QUIC connection management with length-delimited frames
- `download.rs`: Chunked file transfer logic
- `peer_db.rs`: Peer tracking and game availability
- `path_validation.rs`: Security for file operations
**Application Layer:**
- **lanspread-tauri-deno-ts**: Tauri desktop application
- Binary + Library crate for the UI layer
- IPC commands: `request_games`, `install_game`, etc.
- Manages `LanSpreadState` for peer controller, game DB, and downloads
- Uses Tauri plugins: logging, shell, dialogs, persistent storage
- Allocates MiMalloc for memory efficiency
### Data Flow
```
User (Tauri UI)
↓ IPC Commands (request_games, install_game)
lanspread-tauri-deno-ts (app layer)
lanspread-peer (P2P engine)
├─ lanspread-mdns (discovers peers via mDNS)
├─ lanspread-proto (constructs P2P messages)
├─ lanspread-compat (reads legacy game databases)
└─ QUIC Network ↔ Other Peers
lanspread-db (game data structures)
Result → Back to UI
```
### Key Architectural Details
1. **Async/Await**: Tokio runtime throughout for non-blocking I/O
2. **Message Passing**: Unbounded channels for inter-component communication (events and commands)
3. **Protocol**: Length-delimited QUIC frames with JSON serialization
4. **Service Discovery**: Automatic mDNS announcement and discovery of peers
5. **File Streaming**: Chunked transfer with path validation for security
6. **Legacy Support**: Backwards compatibility with ETI game database format
7. **Error Handling**: Custom error types via `eyre` crate
## Development Practices
### From AGENTS.md
- Always check code with `cargo clippy` and fix any issues
- Always format with `cargo +nightly fmt` after completing changes
- Use appropriate Rust log levels when debugging:
- `RUST_LOG=lanspread=debug` for module-specific debugging
- `RUST_LOG=info,lanspread=debug` for general info + module debug
### Performance Considerations
- Use `release-lto` profile for final builds to enable Link-Time Optimization
- MiMalloc is used for the Tauri app to reduce memory overhead
- QUIC protocol chosen for efficient P2P communication
- Length-delimited framing reduces message parsing overhead
## Important Implementation Notes
### Logging
The codebase uses `tracing` and `log` crates. Note: There's documented uncertainty about their exact relationship (see LESSONS_LEARNED.md). The client app uses `log` crate; be consistent with whichever is used in the component you're modifying.
### Legacy Database Support
Some users may have old ETI format game databases. The `lanspread-compat` crate handles reading these. When querying games, both modern and legacy formats are considered.
### Path Validation
File transfers validate paths to prevent directory traversal attacks. All file operations use validated paths from `lanspread-peer`.
### Retry Logic
`lanspread-peer` includes retry logic for requesting games from peers. This handles transient network failures in P2P discovery.
## Known Design Decisions
**Why not Tauri + Leptos?** Leptos adds unnecessary complexity. Tauri is designed to transfer backend Rust to frontend JavaScript world, but with Leptos the frontend becomes Rust, creating a double translation. The current Tauri + Deno/TypeScript approach is cleaner.
**Why Tauri?** Simple setup, easy development with `cargo tauri dev`, easy testing and bundling with installers, small final binary (~13MB).
+12 -27
View File
@@ -2,7 +2,13 @@
## Description ## Description
Simple server and GUI for LAN parties. Peer-to-peer game library sharing for LAN parties.
- Peers let users browse and download games from each other
- they discover each other on the local network via mDNS
- they exchange library metadata over QUIC
Ships as a Tauri desktop app.
## Development ## Development
@@ -15,33 +21,12 @@ cargo install tauri-cli
# install Deno with a package manager or from https://deno.land/ # install Deno with a package manager or from https://deno.land/
``` ```
### Build ### Build or Run
#### Frontend
```bash ```bash
# Development # build
cargo tauri dev # prefix with RUST_LOG=your_module=debug or similary for more verbose output just build
# Production but for testing and without bundling # run
cargo tauri build --no-bundle just run
# Production
cargo tauri build -- --profile release-lto # also bundles everything into a nice platform-specific installer
# on wayland you probably need to set this env var
WEBKIT_DISABLE_DMABUF_RENDERER=1
# update frontend dependencies
deno outdated --update --latest
```
#### Backend
```bash
# Development
./server.sh [options...] # prefix with RUST_LOG=your_module=debug or similary for more verbose output
# Production
cargo build --profile release-lto -p lanspread-server
``` ```
-2
View File
@@ -1,2 +0,0 @@
- `FIRST_START_DONE_FILE: &str = ".softlan_first_start_done"` has to be ignored in game files
- folder within folder with game_id has to be ignored in game files (that's the actual installed game, right?)
+16
View File
@@ -5,6 +5,7 @@ the current idea: mDNS discovery, QUIC transport, on-demand metadata, and
chunked file transfers. chunked file transfers.
## Goals (unchanged) ## Goals (unchanged)
- Local LAN discovery via mDNS. - Local LAN discovery via mDNS.
- QUIC + JSON messages for control, raw streams for file data. - QUIC + JSON messages for control, raw streams for file data.
- UI drives operations through `PeerCommand`, peers remain headless. - UI drives operations through `PeerCommand`, peers remain headless.
@@ -13,6 +14,7 @@ chunked file transfers.
## Peer lifecycle and message flow ## Peer lifecycle and message flow
### 1) Startup and advertise ### 1) Startup and advertise
- Start QUIC server. - Start QUIC server.
- Advertise via mDNS with TXT records: - Advertise via mDNS with TXT records:
- `peer_id` (stable ID, not tied to IP) - `peer_id` (stable ID, not tied to IP)
@@ -21,7 +23,9 @@ chunked file transfers.
- optional `hostname` - optional `hostname`
### 2) Discovery and handshake ### 2) Discovery and handshake
When a peer is discovered: When a peer is discovered:
1. Connect and send `Hello { peer_id, proto_ver, library_rev, library_digest, features }`. 1. Connect and send `Hello { peer_id, proto_ver, library_rev, library_digest, features }`.
2. Receive `HelloAck { peer_id, proto_ver, library_rev, library_digest, features }`. 2. Receive `HelloAck { peer_id, proto_ver, library_rev, library_digest, features }`.
3. If the remote `peer_id` is already known but the address changed, update it. 3. If the remote `peer_id` is already known but the address changed, update it.
@@ -32,11 +36,13 @@ When a peer is discovered:
- Otherwise request `LibrarySnapshot`. - Otherwise request `LibrarySnapshot`.
### 3) Steady state ### 3) Steady state
- Any message updates `last_seen`. - Any message updates `last_seen`.
- Pings run only when idle (or on a longer interval), not every 5 seconds. - Pings run only when idle (or on a longer interval), not every 5 seconds.
- Library updates are pushed as deltas, debounced and coalesced. - Library updates are pushed as deltas, debounced and coalesced.
### 4) Shutdown ### 4) Shutdown
- Optional `Goodbye { peer_id }` lets others remove the peer quickly. - Optional `Goodbye { peer_id }` lets others remove the peer quickly.
- If a peer vanishes without goodbye, stale timeout + ping removal handle it. - If a peer vanishes without goodbye, stale timeout + ping removal handle it.
- Goodbye is a hint, never required for correctness. - Goodbye is a hint, never required for correctness.
@@ -44,20 +50,24 @@ When a peer is discovered:
## Library sync protocol ## Library sync protocol
### Summary and snapshot ### Summary and snapshot
- `LibrarySummary { library_rev, library_digest, game_count }` - `LibrarySummary { library_rev, library_digest, game_count }`
- `LibrarySnapshot { library_rev, games: Vec<GameSummary> }` - `LibrarySnapshot { library_rev, games: Vec<GameSummary> }`
### Delta updates ### Delta updates
- `LibraryDelta { from_rev, to_rev, added, updated, removed }` - `LibraryDelta { from_rev, to_rev, added, updated, removed }`
- `removed` is a list of game IDs. - `removed` is a list of game IDs.
- Deltas are idempotent; ignore if `to_rev` <= known rev. - Deltas are idempotent; ignore if `to_rev` <= known rev.
### GameSummary (concept) ### GameSummary (concept)
- `id`, `name`, `eti_version`, `size`, `downloaded`, `installed` - `id`, `name`, `eti_version`, `size`, `downloaded`, `installed`
- `manifest_hash` (hash of file list + sizes) - `manifest_hash` (hash of file list + sizes)
- `availability` (e.g., `ready`, `downloading`, `local_only`) - `availability` (e.g., `ready`, `downloading`, `local_only`)
## When peers broadcast their game list ## When peers broadcast their game list
- Only on changes, not on a timer. - Only on changes, not on a timer.
- Use a short debounce (1-2 seconds) to coalesce bursts of filesystem events. - Use a short debounce (1-2 seconds) to coalesce bursts of filesystem events.
- Send `LibraryDelta` to known peers; send `LibrarySummary` on new connections. - Send `LibraryDelta` to known peers; send `LibrarySummary` on new connections.
@@ -65,6 +75,7 @@ When a peer is discovered:
## Local game scanning: fast and low cost ## Local game scanning: fast and low cost
### Strategy ### Strategy
1. Maintain a persistent on-disk index (per game): 1. Maintain a persistent on-disk index (per game):
- `manifest_hash`, total size, file list (optional), and a fingerprint - `manifest_hash`, total size, file list (optional), and a fingerprint
(version.ini mtime, .eti mtime/size, local install dir presence). (version.ini mtime, .eti mtime/size, local install dir presence).
@@ -73,6 +84,7 @@ When a peer is discovered:
missed events. missed events.
### Fast-path scanning ### Fast-path scanning
- On startup, list only top-level game directories. - On startup, list only top-level game directories.
- For each game, read a cheap fingerprint: - For each game, read a cheap fingerprint:
- `.eti` size + mtime - `.eti` size + mtime
@@ -82,20 +94,24 @@ When a peer is discovered:
- Only run a recursive scan for new or changed games. - Only run a recursive scan for new or changed games.
### Result ### Result
Most scans become O(number of game dirs), with full recursion only when needed. Most scans become O(number of game dirs), with full recursion only when needed.
## File manifests and downloads ## File manifests and downloads
- Keep `GetGame`/manifest requests, but keyed by `manifest_hash` so repeated - Keep `GetGame`/manifest requests, but keyed by `manifest_hash` so repeated
calls can be skipped when unchanged. calls can be skipped when unchanged.
- Downloads remain chunked QUIC streams with the existing integrity checks. - Downloads remain chunked QUIC streams with the existing integrity checks.
## Fault tolerance rules ## Fault tolerance rules
- Every peer is keyed by `peer_id`, not by IP address. - Every peer is keyed by `peer_id`, not by IP address.
- `library_rev` is monotonic and guards against out-of-order updates. - `library_rev` is monotonic and guards against out-of-order updates.
- Any mismatch or missing delta falls back to `LibrarySnapshot`. - Any mismatch or missing delta falls back to `LibrarySnapshot`.
- Loss of goodbye is harmless; stale timeout is authoritative. - Loss of goodbye is harmless; stale timeout is authoritative.
## Roadmap from current design to this one ## Roadmap from current design to this one
1. Protocol updates in `lanspread-proto`: 1. Protocol updates in `lanspread-proto`:
- Define `Hello`, `HelloAck`, `LibrarySummary`, `LibrarySnapshot`, - Define `Hello`, `HelloAck`, `LibrarySummary`, `LibrarySnapshot`,
`LibraryDelta`, and optional `Goodbye` messages. `LibraryDelta`, and optional `Goodbye` messages.
-7
View File
@@ -1,7 +0,0 @@
# Tauri + React + Typescript
This template should help get you started developing with Tauri, React and Typescript in Vite.
## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)
+4 -7
View File
@@ -1,15 +1,12 @@
export RUSTFLAGS := "-C target-cpu=native" export RUSTFLAGS := "-C target-cpu=native"
export WEBKIT_DISABLE_COMPOSITING_MODE := "1" export WEBKIT_DISABLE_COMPOSITING_MODE := "1"
server: build:
cargo build -p lanspread-server
client:
cargo tauri dev
buildclient:
cargo tauri build --no-bundle -- --profile dev cargo tauri build --no-bundle -- --profile dev
run:
cargo tauri dev
fmt: fmt:
cargo +nightly fmt cargo +nightly fmt