Full catalog generation previously prepared each game sequentially, so a version mismatch late in the catalog could surface only after earlier packages had already been extracted and hashed. Validate every selected version.ini against game.db before starting manifest preparation, while retaining the per-package validation during preparation to detect later input changes. Document the ordering and cover it with a regression where an earlier archive would fail if archive processing began before a later version mismatch. Test Plan: - `just fmt` -- passed - `just test` -- passed - `just clippy` -- passed - `git diff --cached --check` -- passed
221 lines
8.9 KiB
Markdown
221 lines
8.9 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 development catalog:
|
|
|
|
```bash
|
|
just run
|
|
```
|
|
|
|
Build without bundling:
|
|
|
|
```bash
|
|
just build
|
|
```
|
|
|
|
Run the standard checks before committing:
|
|
|
|
```bash
|
|
just fmt
|
|
just clippy
|
|
just test
|
|
just frontend-test
|
|
```
|
|
|
|
`just run` and `just build` first check the default development catalog. The
|
|
workspace Rust commands explicitly opt into that exact fixture resource map so
|
|
Tauri's build script does not mistake test data for production authority.
|
|
|
|
## 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 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 run` | Check the default fixture catalog and run the GUI. |
|
|
| `just build` | 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 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. |
|