Files
lanspread/README.md
T
ddidderr 3bbfe919d2 docs(build): document real-data workflows
Clarify that `just build` is the production no-bundle launcher build, while
fixture builds are explicit. Document package-directory generation, the
`LANSPREAD_GAMES_DIR` and `--set GAMES_DIR` default-run forms, the metadata
cache behavior, and the force-refresh override so the catalogue workflow is
usable without consulting the recipe implementation.

Test Plan:
- `prettier --check --prose-wrap always --print-width 80 README.md CLAUDE.md` -- passed
- `just --fmt --check` -- passed
- `git diff --cached --check` -- passed
2026-08-20 09:06:47 +02:00

262 lines
11 KiB
Markdown

# lanspread
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.
## Peer identity and protocol status
Each desktop installation creates and reuses one Ed25519 identity in its Tauri
app-data directory. Its `PeerId` is lowercase unpadded RFC 4648 base32 of BLAKE3
over the certificate's complete canonical DER SubjectPublicKeyInfo; certificates
and private keys are never distributed from the repository. Outbound QUIC
connections authenticate the responder against the exact expected `PeerId`. Wire
protocol 8 (`lanspread/8`) is the only supported protocol. Peers exchange
responder-owned, revisioned snapshots over identity-pinned endpoints; file and
streamed-install requests name the catalog's exact `ContentId`. There are no
legacy wire fallbacks or repository-shared identity keys.
## First-time setup
Install Rust, Deno, and `just` first, then bootstrap the project:
```bash
just setup
```
That installs the Tauri CLI and the locked frontend dependencies. All routine
repository work should go through `just`; the recipes carry the catalog mode and
Tauri resource configuration needed to keep fixture authority out of production
builds.
## Daily development
Run the desktop app with the checked-in test catalog:
```bash
just run
```
Build a launcher from the already-published production catalog without creating
an installer:
```bash
just build
```
Generate the production catalog from a package tree when necessary, then build
the same no-bundle launcher:
```bash
just build-production /srv/lanspread/packages
```
To make the default `just` command use a real package tree, set the directory
before invoking it. The command generates or reuses the production catalog and
then runs the launcher with the production resource map:
```bash
LANSPREAD_GAMES_DIR=/srv/lanspread/packages just
# Equivalent Just-variable form:
just --set GAMES_DIR /srv/lanspread/packages
```
The fixture-only paths remain explicit:
```bash
just run-fixture
just build-fixture
```
Run the standard checks before committing:
```bash
just fmt
just clippy
just test
just frontend-test
```
`just run` uses the fixture path only when `LANSPREAD_GAMES_DIR` is unset.
`just build` always uses the production resource map and production profile; use
`just build-fixture` when a fixture build is intended. Tauri's build script
still rejects fixture resources unless the explicit fixture opt-in is present.
## Catalog checksums and production manifests
Lanspread does not trust file descriptions received from peers. The release
catalog contains the expected bytes for each game version. The catalog publisher
derives that authority from the canonical game packages and writes:
- one `<game_id>.json` manifest containing every ordinary file's size, full-file
BLAKE3, and BLAKE3 for each 128 MiB transfer chunk;
- BLAKE3 and size for every file obtained by extracting the root-level `.eti`
archives, which authorizes Stream Install output; and
- `catalog-content-index-v1.jsonl`, which binds every catalog game/version to
its complete manifest `ContentId` and Stream Install capability.
The `ContentId` is a digest of the canonical manifest transcript, not merely a
hash of one archive. Never calculate or edit these values by hand.
### Prepare the package tree
Place the canonical packages under one directory, with each direct child named
exactly like the `game_id` in the production `game.db`:
```text
/srv/lanspread/packages/
├── alienswarm/
│ ├── version.ini
│ ├── alienswarm.eti
│ └── ...
├── bf1942/
│ ├── version.ini
│ ├── bf1942.eti
│ └── ...
└── ...
```
Each `version.ini` must match that game's `game_version` in `game.db`. Package
entries must be regular files/directories with canonical portable names; links,
reparse points, special files, aliases, unsafe archive output, or changing bytes
make generation fail closed.
The default publisher uses the checked-in Linux `unrar` sidecar. Select another
trusted executable when generating on a different supported host:
```bash
LANSPREAD_UNRAR=/absolute/path/to/unrar \
just catalog-generate-production /srv/lanspread/packages
```
### Generate and verify all checksums
Generate the complete production manifest set:
```bash
just catalog-generate-production /srv/lanspread/packages
```
The `--all` recipe keeps a local metadata-only stamp in
`.lanspread/catalog-cache/production.json`. It compares the package tree's
paths, entry types, sizes, and nanosecond mtimes, plus the catalog database and
`unrar` metadata. If those values and the published output still match, the
expensive package hashing and extraction step is skipped. This is only a
performance cache: `catalog-check-production` and every production build still
validate the catalog authority. Force a refresh when needed:
```bash
LANSPREAD_CATALOG_FORCE=1 just catalog-generate-production /srv/lanspread/packages
```
The publisher first checks every selected package's `version.ini` against
`game.db`, then independently reads and hashes each selected package twice
before publication. Under a durable publication marker, it atomically replaces
each manifest and writes the complete content index last. Interruption leaves
the marker in place so later checks and builds fail closed instead of accepting
a mixed generation.
Verify the published database/index/manifest set without rereading the package
corpus:
```bash
just catalog-check-production
```
This is a packaging integrity check, not a substitute for retaining the
canonical packages and the successful generation log as provenance. The output
prints the `game_id`, `game_version`, and `content_id` for every checked game.
### Regenerate one changed game
After a complete manifest set exists, update one game without rehashing every
package:
```bash
just catalog-generate-production-game /srv/lanspread/packages alienswarm
just catalog-check-production
```
Incremental generation refuses to bootstrap a partial catalog. Under its durable
publication marker it revalidates every unselected manifest/index identity,
rebuilds the complete index, and then publishes the selected game. Use the full
generation command for a new catalog or any intentionally broad catalog change.
### Build a production bundle
Only after the production check succeeds, create the installer/bundle:
```bash
just bundle
```
`just bundle` repeats the production catalog gate before Tauri packaging. It
never falls back to fixture manifests. This checkout does not include the
canonical 186-game package corpus, so the command intentionally fails until that
external corpus has been processed into
`crates/lanspread-tauri-deno-ts/src-tauri/manifests/`.
## Fixture catalogs and peer-CLI acceptance
The committed fixture profiles are test authority only:
```bash
just fixture-catalog-check # default GUI/CLI profile
just fixture-catalogs-check # default + solid + multi + unknown profiles
just fixture-catalogs # explicitly regenerate all committed profiles
```
The solid, multi-archive, and unknown-game profiles intentionally describe
different package bytes, so the peer-CLI matrix checks all of them before
building its Docker image. Normal GUI development needs only the default
profile.
Build or run the JSONL peer harness:
```bash
just peer-cli-build
just peer-cli-image
just peer-cli-tests S1 S2 S3
just peer-cli-run alpha
```
Run the entire S1-S49 acceptance matrix by omitting scenario arguments:
```bash
just peer-cli-tests
```
For an interactive three-peer session, start these in separate terminals:
```bash
just peer-cli-alpha
just peer-cli-bravo
just peer-cli-charlie
```
## Important just commands
| Command | Purpose |
| --------------------------------------------------- | ------------------------------------------------------------------------ |
| `just setup` | Install the Tauri CLI and frontend dependencies. |
| `just` / `just run` | Run fixtures, or real data when `LANSPREAD_GAMES_DIR` is set. |
| `just run-fixture` | Check the default fixture catalog and run the GUI. |
| `just build` | Validate production authority and build without bundling. |
| `just build-production PACKAGES_DIR` | Generate/reuse production authority and build without bundling. |
| `just build-fixture` | Check the default fixture catalog and build without bundling. |
| `just bundle` | Check production authority and build production bundles. |
| `just catalog-generate-production PACKAGES_DIR` | Hash, cache, and publish the complete production package corpus. |
| `just catalog-generate-production-game DIR GAME_ID` | Safely regenerate one game in an already complete catalog. |
| `just catalog-check-production` | Validate all production database/index/manifest artifacts. |
| `just fixture-catalog-check` | Validate the default development fixture catalog. |
| `just fixture-catalogs-check` | Validate every peer-CLI fixture profile. |
| `just fixture-catalogs` | Regenerate all committed fixture profiles from their packages. |
| `just fmt` | Format Rust, TOML, Markdown, and the Justfile. |
| `just clippy` | Lint every Rust workspace target with warnings denied. |
| `just test` | Run every Rust workspace test target. |
| `just frontend-test` | Run the frontend reducer/lifecycle tests. |
| `just peer-cli-build` | Build the JSONL peer harness. |
| `just peer-cli-image` | Check fixture authority and build the harness Docker image. |
| `just peer-cli-tests [SCENARIO ...]` | Run selected scenarios, or the complete matrix when no IDs are supplied. |
| `just peer-cli-run NAME` | Start one persistent-state interactive harness peer. |