markdown formatting
This commit is contained in:
+148
-150
@@ -5,10 +5,9 @@
|
||||
Revised implementation plan; not yet implemented.
|
||||
|
||||
This plan deliberately treats Lanspread as what it is: a desktop utility for
|
||||
friends and other attendees at a LAN party to discover each other, share a
|
||||
known game catalog at LAN speed, and coordinate a match. It is not an account
|
||||
system, a global untrusted file-sharing network, or a device-administration
|
||||
product.
|
||||
friends and other attendees at a LAN party to discover each other, share a known
|
||||
game catalog at LAN speed, and coordinate a match. It is not an account system,
|
||||
a global untrusted file-sharing network, or a device-administration product.
|
||||
|
||||
The normal user journey must remain:
|
||||
|
||||
@@ -18,9 +17,9 @@ The normal user journey must remain:
|
||||
4. Let Lanspread swarm from matching peers and verify the result itself.
|
||||
5. Use Call to Play while those people are present.
|
||||
|
||||
Security mechanisms in this plan are automatic. There are no key backup
|
||||
dialogs, trust ceremonies, fingerprint prompts, or per-device download
|
||||
permissions in the normal UI.
|
||||
Security mechanisms in this plan are automatic. There are no key backup dialogs,
|
||||
trust ceremonies, fingerprint prompts, or per-device download permissions in the
|
||||
normal UI.
|
||||
|
||||
The project still has one current wire version and no compatibility shims. The
|
||||
wire changes below are developed together and activated with one protocol bump,
|
||||
@@ -28,16 +27,16 @@ not three partially compatible protocol generations.
|
||||
|
||||
## 1. Product and architecture decisions
|
||||
|
||||
| Area | Decision | User-visible result |
|
||||
|---|---|---|
|
||||
| Filesystem safety | Validate the complete destination manifest before any mutation and confine it to one catalog game root. | A hostile peer cannot overwrite another game, `local/`, saves, or transaction state. |
|
||||
| Content authority | Ship BLAKE3 file and chunk hashes from the same bundled catalog authority as `game.db`. | Every eligible nearby peer is usable automatically; wrong bytes are rejected and retried elsewhere. |
|
||||
| Peer identity | Use one installation-local TLS key and derive `PeerId` from that TLS public key. | Identity works silently and survives ordinary restarts when possible; users do not manage it. |
|
||||
| Transport | Pin every outbound QUIC connection to the expected `PeerId`. | An address spoof or MITM cannot impersonate the peer selected as a source. |
|
||||
| Control messages | Use ordinary bounded protocol messages inside TLS. Treat unauthenticated inbound change notifications only as hints that trigger a pinned pull, and carry current revisions on the liveness ping that already runs so a lost hint self-heals. | No signed-envelope layer, nonce ledger, or message-signing overhead. |
|
||||
| Call to Play | Exchange only each peer's own session state by direct pinned pulls; do not relay third-party histories. | Calls are live LAN-party state and disappear naturally as their authors leave. |
|
||||
| Privacy | Provide one global Local network sharing switch. | Participation is easy to understand; no per-peer policy matrix. |
|
||||
| Protocol rollout | Make one cutover to the new current protocol. | Mixed versions are explained clearly, without maintaining legacy paths. |
|
||||
| Area | Decision | User-visible result |
|
||||
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| Filesystem safety | Validate the complete destination manifest before any mutation and confine it to one catalog game root. | A hostile peer cannot overwrite another game, `local/`, saves, or transaction state. |
|
||||
| Content authority | Ship BLAKE3 file and chunk hashes from the same bundled catalog authority as `game.db`. | Every eligible nearby peer is usable automatically; wrong bytes are rejected and retried elsewhere. |
|
||||
| Peer identity | Use one installation-local TLS key and derive `PeerId` from that TLS public key. | Identity works silently and survives ordinary restarts when possible; users do not manage it. |
|
||||
| Transport | Pin every outbound QUIC connection to the expected `PeerId`. | An address spoof or MITM cannot impersonate the peer selected as a source. |
|
||||
| Control messages | Use ordinary bounded protocol messages inside TLS. Treat unauthenticated inbound change notifications only as hints that trigger a pinned pull, and carry current revisions on the liveness ping that already runs so a lost hint self-heals. | No signed-envelope layer, nonce ledger, or message-signing overhead. |
|
||||
| Call to Play | Exchange only each peer's own session state by direct pinned pulls; do not relay third-party histories. | Calls are live LAN-party state and disappear naturally as their authors leave. |
|
||||
| Privacy | Provide one global Local network sharing switch. | Participation is easy to understand; no per-peer policy matrix. |
|
||||
| Protocol rollout | Make one cutover to the new current protocol. | Mixed versions are explained clearly, without maintaining legacy paths. |
|
||||
|
||||
The resulting data flow is intentionally small:
|
||||
|
||||
@@ -58,31 +57,31 @@ pinned liveness ping -> responder's own current revisions
|
||||
## 2. Threat model and guarantees
|
||||
|
||||
Assume a hostile device can join the same LAN, advertise arbitrary mDNS data,
|
||||
send arbitrary protocol messages, occupy reused IP addresses, and run a
|
||||
modified Lanspread build. The attacker does not control the victim's OS, the
|
||||
installed Lanspread application, or its bundled catalog files.
|
||||
send arbitrary protocol messages, occupy reused IP addresses, and run a modified
|
||||
Lanspread build. The attacker does not control the victim's OS, the installed
|
||||
Lanspread application, or its bundled catalog files.
|
||||
|
||||
After this plan:
|
||||
|
||||
- a remote description cannot make Lanspread create, truncate, or delete a
|
||||
path outside the requested catalog game's download-owned area;
|
||||
- a selected responder must prove possession of the TLS private key whose
|
||||
public key derives the expected `PeerId`;
|
||||
- a remote description cannot make Lanspread create, truncate, or delete a path
|
||||
outside the requested catalog game's download-owned area;
|
||||
- a selected responder must prove possession of the TLS private key whose public
|
||||
key derives the expected `PeerId`;
|
||||
- mDNS, IP addresses, display names, and inbound notification bodies never
|
||||
become identity authority by themselves;
|
||||
- a source cannot make a download commit bytes that differ from the hashes in
|
||||
the victim's bundled catalog, even if that source is the only peer present;
|
||||
- corrupt sources are removed from the current transfer automatically rather
|
||||
than presented to the user as a trust decision; and
|
||||
- one peer cannot publish Call-to-Play actions as another peer or mutate
|
||||
another peer's author-owned state.
|
||||
- one peer cannot publish Call-to-Play actions as another peer or mutate another
|
||||
peer's author-owned state.
|
||||
|
||||
The following are explicit non-goals:
|
||||
|
||||
- A `PeerId` does not prove a human name. Display names remain friendly labels.
|
||||
- The installation key is not a user account and has no promised continuity
|
||||
across OS reinstall, application-data deletion, or copying the application
|
||||
to another computer.
|
||||
across OS reinstall, application-data deletion, or copying the application to
|
||||
another computer.
|
||||
- Content hashes prove that bytes match the bundled catalog. They do not prove
|
||||
that the catalog publisher's game is benign, licensed, or malware-free.
|
||||
- A hash advertised by the same peer that sends the bytes is not trusted. The
|
||||
@@ -113,11 +112,11 @@ Validation is for the complete list and fails without any mutation. It must:
|
||||
|
||||
- require a known catalog `game_id` and resolve every destination relative to
|
||||
exactly `<games_folder>/<game_id>`;
|
||||
- use one canonical forward-slash relative-path form and reject empty,
|
||||
absolute, drive-qualified, UNC, NUL, `.`, `..`, mixed-separator, and
|
||||
non-normalized paths;
|
||||
- reject duplicate paths, file/directory conflicts, and platform aliases such
|
||||
as Windows case, trailing-dot/space, device-name, and alternate-data-stream
|
||||
- use one canonical forward-slash relative-path form and reject empty, absolute,
|
||||
drive-qualified, UNC, NUL, `.`, `..`, mixed-separator, and non-normalized
|
||||
paths;
|
||||
- reject duplicate paths, file/directory conflicts, and platform aliases such as
|
||||
Windows case, trailing-dot/space, device-name, and alternate-data-stream
|
||||
collisions;
|
||||
- reject `local/`, `.local.*`, download/install intent state, legacy state,
|
||||
scratch sentinels, and every other path owned by installation or recovery,
|
||||
@@ -133,17 +132,17 @@ The Tauri command supplies only the selected `game_id`. The peer core chooses
|
||||
the complete authoritative plan. A UI-echoed file list is never authority.
|
||||
|
||||
After a successful complete transfer, remove download-owned files absent from
|
||||
the authoritative manifest before committing `version.ini`. Preserve
|
||||
`local/`, install staging/backup state, and user-owned files in all success,
|
||||
failure, cancellation, and recovery paths.
|
||||
the authoritative manifest before committing `version.ini`. Preserve `local/`,
|
||||
install staging/backup state, and user-owned files in all success, failure,
|
||||
cancellation, and recovery paths.
|
||||
|
||||
For the current protocol, this validator safely contains the existing remote
|
||||
descriptions. A narrow protocol-7 adapter requires and removes exactly one
|
||||
matching leading `game_id/` component (and discards only the current exact
|
||||
redundant game-root directory entry) before constructing root-relative paths;
|
||||
it rejects a missing/different/doubled prefix. After the protocol cutover, the
|
||||
same validated type is constructed directly from the bundled content manifest
|
||||
and remote descriptions cease to define local paths at all.
|
||||
redundant game-root directory entry) before constructing root-relative paths; it
|
||||
rejects a missing/different/doubled prefix. After the protocol cutover, the same
|
||||
validated type is constructed directly from the bundled content manifest and
|
||||
remote descriptions cease to define local paths at all.
|
||||
|
||||
Required proof includes hostile descriptors placed after valid descriptors,
|
||||
cross-game paths, both requested and other-game `local/` sentinels, reserved
|
||||
@@ -153,10 +152,10 @@ mutation.
|
||||
|
||||
### 3.2 Make the bundled catalog the content authority
|
||||
|
||||
`game.db` is already the application's authority for game identity and
|
||||
version. Add reproducibly generated per-game companion manifest artifacts,
|
||||
located at `manifests/<game_id>.json` (loaded on-demand when downloading or serving a game),
|
||||
and package them with both the desktop application and peer-CLI fixtures.
|
||||
`game.db` is already the application's authority for game identity and version.
|
||||
Add reproducibly generated per-game companion manifest artifacts, located at
|
||||
`manifests/<game_id>.json` (loaded on-demand when downloading or serving a
|
||||
game), and package them with both the desktop application and peer-CLI fixtures.
|
||||
|
||||
For each supported `(game_id, game_version)`, the manifest artifact contains:
|
||||
|
||||
@@ -176,11 +175,11 @@ CatalogContentManifest {
|
||||
}
|
||||
```
|
||||
|
||||
Entries are sorted by canonical path. `content_id` is BLAKE3 over a
|
||||
versioned, length-delimited encoding of all preceding manifest fields and
|
||||
hashes, excluding the `content_id` field itself; it is not the current
|
||||
noncryptographic `u64 manifest_hash`. Golden tests freeze that encoding. The
|
||||
ordinary chunk size matches Lanspread's 128 MiB transfer chunk.
|
||||
Entries are sorted by canonical path. `content_id` is BLAKE3 over a versioned,
|
||||
length-delimited encoding of all preceding manifest fields and hashes, excluding
|
||||
the `content_id` field itself; it is not the current noncryptographic
|
||||
`u64 manifest_hash`. Golden tests freeze that encoding. The ordinary chunk size
|
||||
matches Lanspread's 128 MiB transfer chunk.
|
||||
|
||||
The catalog publishing workflow must generate these per-game manifests from the
|
||||
canonical game packages, verify them by rereading the packages, and fail the
|
||||
@@ -193,22 +192,22 @@ games.
|
||||
Peers advertise only that they can serve a catalog `content_id`. A peer counts
|
||||
as a source for the local catalog game only when its advertised ID exactly
|
||||
matches the receiver's expected ID. The receiver builds paths, sizes, chunks,
|
||||
and expected hashes entirely from its local catalog manifest. This replaces remote
|
||||
manifest selection and majority-by-file-size consensus.
|
||||
and expected hashes entirely from its local catalog manifest. This replaces
|
||||
remote manifest selection and majority-by-file-size consensus.
|
||||
|
||||
For ordinary downloads:
|
||||
|
||||
1. Select every currently reachable peer advertising the expected
|
||||
`content_id`; there is no approval prompt.
|
||||
1. Select every currently reachable peer advertising the expected `content_id`;
|
||||
there is no approval prompt.
|
||||
2. Carry `PeerEndpoint { peer_id, addr }` and `content_id` through planning,
|
||||
swarming, progress, and retry.
|
||||
3. The sender serves only an exact catalog file/range for the requested
|
||||
`(game_id, content_id)` and applies the same canonical/reserved-path policy
|
||||
before opening a local file. A caller-supplied path can never expose
|
||||
`local/` or another local file.
|
||||
4. Hash each chunk with BLAKE3 while receiving it and compare it before marking that chunk
|
||||
complete. Exact length, offset coverage, and the catalog file shape are also
|
||||
mandatory.
|
||||
before opening a local file. A caller-supplied path can never expose `local/`
|
||||
or another local file.
|
||||
4. Hash each chunk with BLAKE3 while receiving it and compare it before marking
|
||||
that chunk complete. Exact length, offset coverage, and the catalog file
|
||||
shape are also mandatory.
|
||||
5. On mismatch, invalidate that write, quarantine that `(PeerId, content_id)`
|
||||
for the current runtime/transfer, and retry the chunk from another matching
|
||||
peer. Do not create durable “trust” state.
|
||||
@@ -216,42 +215,43 @@ For ordinary downloads:
|
||||
successfully. Failure leaves the game non-downloadable/non-installable and
|
||||
preserves `local/`.
|
||||
|
||||
No background disk-scanning or pre-hashing of existing files is required. Chunks are verified strictly as they stream in during an active transfer.
|
||||
No background disk-scanning or pre-hashing of existing files is required. Chunks
|
||||
are verified strictly as they stream in during an active transfer.
|
||||
|
||||
Streamed install needs a catalog-owned extracted-file manifest because the
|
||||
sender controls both today's RAR CRC32 metadata and extracted bytes. The
|
||||
receiver accepts exactly the expected path set, sizes, and BLAKE3 values in
|
||||
isolated staging, then applies the documented local account/language rewrite
|
||||
and promotes the transaction. CRC32 may remain as an early corruption check,
|
||||
but it is not the security boundary. A game without a verified extracted
|
||||
manifest does not offer Stream Install; there is no unverified fallback or
|
||||
warning-through button.
|
||||
isolated staging, then applies the documented local account/language rewrite and
|
||||
promotes the transaction. CRC32 may remain as an early corruption check, but it
|
||||
is not the security boundary. A game without a verified extracted manifest does
|
||||
not offer Stream Install; there is no unverified fallback or warning-through
|
||||
button.
|
||||
|
||||
Hashing is performed in the existing streaming I/O path. The acceptance gate
|
||||
measures end-to-end throughput on the standard LAN workload and avoids a
|
||||
second full read when complete chunk coverage already proves the file bytes.
|
||||
measures end-to-end throughput on the standard LAN workload and avoids a second
|
||||
full read when complete chunk coverage already proves the file bytes.
|
||||
|
||||
### 3.3 Use a simple installation-local TLS identity
|
||||
|
||||
The identity exists to bind a live peer and its changing address to TLS. It is
|
||||
not exposed as a user credential.
|
||||
|
||||
- Generate one self-issued TLS certificate/key pair in Tauri's
|
||||
`app_data_dir()` and store it in one versioned application file with
|
||||
restrictive permissions where the platform supports them.
|
||||
- Generate one self-issued TLS certificate/key pair in Tauri's `app_data_dir()`
|
||||
and store it in one versioned application file with restrictive permissions
|
||||
where the platform supports them.
|
||||
- Prefer Ed25519 if the selected s2n-quic rustls provider supports the complete
|
||||
responder-verification path. Otherwise use one supported P-256 TLS key. Do not
|
||||
add a second signing identity or a custom certificate-extension binding.
|
||||
- Define `PeerId` as lowercase unpadded base32 of
|
||||
`BLAKE3(canonical DER SubjectPublicKeyInfo)` from the actual TLS key. The
|
||||
same key is therefore both the identity and the TLS proof-of-possession key.
|
||||
- Validate on load that the private key, certificate SPKI, and derived ID
|
||||
agree. Never log private material.
|
||||
- A valid file is reused. A missing or corrupt file is regenerated
|
||||
automatically (quarantining corrupt bytes best-effort) and produces at most
|
||||
a diagnostic log entry. If persistence is unavailable, use a fresh in-memory
|
||||
identity for that run and show a non-blocking diagnostic; LAN functionality
|
||||
should not become a repair wizard.
|
||||
`BLAKE3(canonical DER SubjectPublicKeyInfo)` from the actual TLS key. The same
|
||||
key is therefore both the identity and the TLS proof-of-possession key.
|
||||
- Validate on load that the private key, certificate SPKI, and derived ID agree.
|
||||
Never log private material.
|
||||
- A valid file is reused. A missing or corrupt file is regenerated automatically
|
||||
(quarantining corrupt bytes best-effort) and produces at most a diagnostic log
|
||||
entry. If persistence is unavailable, use a fresh in-memory identity for that
|
||||
run and show a non-blocking diagnostic; LAN functionality should not become a
|
||||
repair wizard.
|
||||
- The peer CLI may accept an explicit deterministic identity file/seed for
|
||||
repeatable tests. It does not probe keyrings or share a default container
|
||||
identity accidentally.
|
||||
@@ -278,11 +278,11 @@ Call-to-Play refresh, metadata/content requests, chunk plans, retries, streamed
|
||||
install, healing, liveness, and direct peer-CLI operations. Delete
|
||||
address-derived IDs, unique-IP identity fallbacks, and address-only connects.
|
||||
|
||||
mDNS supplies bounded candidates containing `(peer_id, addr, protocol,
|
||||
revision hints)`. It may cause a dial, but it never directly creates or
|
||||
updates authenticated peer/library/Call-to-Play state. A candidate becomes a
|
||||
peer only after a successful outgoing TLS connection to its advertised address
|
||||
proves the expected `PeerId`.
|
||||
mDNS supplies bounded candidates containing
|
||||
`(peer_id, addr, protocol, revision hints)`. It may cause a dial, but it never
|
||||
directly creates or updates authenticated peer/library/Call-to-Play state. A
|
||||
candidate becomes a peer only after a successful outgoing TLS connection to its
|
||||
advertised address proves the expected `PeerId`.
|
||||
|
||||
Use TLS 1.3 with a self-issued certificate. The custom client verifier must:
|
||||
|
||||
@@ -291,17 +291,17 @@ Use TLS 1.3 with a self-issued certificate. The custom client verifier must:
|
||||
endpoint's expected ID; and
|
||||
3. perform real TLS 1.3 CertificateVerify validation under the presented key.
|
||||
|
||||
The load-bearing negative test presents peer A's certificate/SPKI with peer
|
||||
B's private key and requires the handshake to fail. Also reject a different
|
||||
valid peer at a reused address. Use one version-bound ALPN, disable 0-RTT, and
|
||||
start without TLS session resumption so every short-lived connection performs
|
||||
the simple full proof.
|
||||
The load-bearing negative test presents peer A's certificate/SPKI with peer B's
|
||||
private key and requires the handshake to fail. Also reject a different valid
|
||||
peer at a reused address. Use one version-bound ALPN, disable 0-RTT, and start
|
||||
without TLS session resumption so every short-lived connection performs the
|
||||
simple full proof.
|
||||
|
||||
The protocol is deliberately responder-authenticated rather than wrapping
|
||||
every message in a signature:
|
||||
The protocol is deliberately responder-authenticated rather than wrapping every
|
||||
message in a signature:
|
||||
|
||||
- Requests that read public library/content state may be made by any LAN
|
||||
client while Local network sharing is enabled.
|
||||
- Requests that read public library/content state may be made by any LAN client
|
||||
while Local network sharing is enabled.
|
||||
- A response is authoritative only to the initiator that connected using the
|
||||
expected `PeerEndpoint`; the TLS channel supplies integrity and request/
|
||||
response correlation.
|
||||
@@ -311,9 +311,9 @@ every message in a signature:
|
||||
For a known claimed ID they schedule one coalesced, rate-limited pull from
|
||||
that ID's already known endpoint. Their payload never merges directly. Hints
|
||||
for unknown IDs are ignored and mDNS remains the discovery path.
|
||||
- `Hello` becomes a pull-oriented exchange: the initiator sends no
|
||||
authoritative identity or replicated state, and the pinned responder returns
|
||||
its own current snapshot.
|
||||
- `Hello` becomes a pull-oriented exchange: the initiator sends no authoritative
|
||||
identity or replicated state, and the pinned responder returns its own current
|
||||
snapshot.
|
||||
|
||||
This extra pull is one small LAN round trip and removes general signed
|
||||
envelopes, canonical opaque payloads, nonce caches, replay semantics, inbound
|
||||
@@ -346,11 +346,11 @@ alone, which it could already achieve by changing nothing.
|
||||
|
||||
An unproven address collision never evicts an authenticated peer. If a pinned
|
||||
dial later proves that a different ID now owns the same address, atomically
|
||||
replace address ownership and retire the old record only if it still names
|
||||
that address/generation. A same-ID address move is likewise committed only
|
||||
after pinning the new endpoint. Pings are also pinned, and a late ping result
|
||||
may update/remove only the same endpoint generation it probed so it cannot
|
||||
delete a peer that has already moved or reconnected.
|
||||
replace address ownership and retire the old record only if it still names that
|
||||
address/generation. A same-ID address move is likewise committed only after
|
||||
pinning the new endpoint. Pings are also pinned, and a late ping result may
|
||||
update/remove only the same endpoint generation it probed so it cannot delete a
|
||||
peer that has already moved or reconnected.
|
||||
|
||||
Remove `Goodbye`. It is unnecessary for correctness and an unauthenticated
|
||||
removal hint is unsafe. mDNS expiry plus responder-pinned liveness handles
|
||||
@@ -392,18 +392,18 @@ Authority rules remain simple:
|
||||
- `Create`, `Start`, `Cancel`, and `AddTime` are effective only when the pinned
|
||||
author equals `CallId.creator`.
|
||||
- RSVP, ready/leave, and chat actions are attributed to the pinned author. They
|
||||
become effective only while the referenced creator root is directly
|
||||
present; an author slice pulled before its creator is retained within its
|
||||
ordinary bound but remains hidden until that creator's direct pull arrives.
|
||||
- A snapshot contains only events authored by its responder. Third-party
|
||||
events are rejected rather than relayed.
|
||||
become effective only while the referenced creator root is directly present;
|
||||
an author slice pulled before its creator is retained within its ordinary
|
||||
bound but remains hidden until that creator's direct pull arrives.
|
||||
- A snapshot contains only events authored by its responder. Third-party events
|
||||
are rejected rather than relayed.
|
||||
- Display names never grant authority.
|
||||
|
||||
A newly arriving peer discovers and pulls directly from every live peer, so it
|
||||
reconstructs calls from the people still present. If an author's peer goes
|
||||
away, remove that author's slice. If the creator goes away, the call disappears
|
||||
from the derived view. A participant who leaves naturally drops out. This is
|
||||
the intended session model, not data loss.
|
||||
reconstructs calls from the people still present. If an author's peer goes away,
|
||||
remove that author's slice. If the creator goes away, the call disappears from
|
||||
the derived view. A participant who leaves naturally drops out. This is the
|
||||
intended session model, not data loss.
|
||||
|
||||
Keep the useful human-scale timers: active calls expire, unresolved expired
|
||||
calls may remain visible for five minutes, and Start/Cancel results may remain
|
||||
@@ -412,13 +412,12 @@ snapshot. There are no session-long tombstones, rootless terminal records,
|
||||
three-day history horizons, verification caches, or permanent anti-resurrection
|
||||
state because no third party can replay an old author's history as authority.
|
||||
|
||||
Retain straightforward schema and resource limits: bounded strings/chat,
|
||||
bounded events and encoded bytes per author, bounded total live peers, and a
|
||||
named control-frame maximum. Validate one author's snapshot off to the side and
|
||||
accept or reject it as a unit; a bad/oversized peer cannot consume another
|
||||
author's slice or the local author's capacity. Exact limits are set from the
|
||||
existing three-peer and stress fixtures, not from an internet-scale adversary
|
||||
model.
|
||||
Retain straightforward schema and resource limits: bounded strings/chat, bounded
|
||||
events and encoded bytes per author, bounded total live peers, and a named
|
||||
control-frame maximum. Validate one author's snapshot off to the side and accept
|
||||
or reject it as a unit; a bad/oversized peer cannot consume another author's
|
||||
slice or the local author's capacity. Exact limits are set from the existing
|
||||
three-peer and stress fixtures, not from an internet-scale adversary model.
|
||||
|
||||
A malicious creator can show inconsistent versions of its own noncritical call
|
||||
to different peers. This plan accepts that limit rather than adding signatures,
|
||||
@@ -428,8 +427,8 @@ gossip, consensus, or permanent storage to a party invitation feature.
|
||||
|
||||
Add one visible `Local network sharing` setting, on by default for this
|
||||
LAN-sharing application. When off, stop mDNS advertisement/discovery, the QUIC
|
||||
listener, outbound refresh, and serving. The setting is durable and its state
|
||||
is obvious in the main UI/settings.
|
||||
listener, outbound refresh, and serving. The setting is durable and its state is
|
||||
obvious in the main UI/settings.
|
||||
|
||||
Do not add per-peer source prompts. Every peer with the locally expected
|
||||
`content_id` is an eligible swarm source; verification is automatic.
|
||||
@@ -441,10 +440,10 @@ Trusted, key-changed, backup, repair, or fingerprint-confirmation workflows.
|
||||
User-facing exceptional states are concrete:
|
||||
|
||||
- `Verifying downloaded chunks` while newly received content is checked;
|
||||
- `A source sent invalid data; retrying another nearby peer` when recovery is
|
||||
in progress;
|
||||
- `No nearby peer could provide the verified catalog version` after all
|
||||
matching sources fail;
|
||||
- `A source sent invalid data; retrying another nearby peer` when recovery is in
|
||||
progress;
|
||||
- `No nearby peer could provide the verified catalog version` after all matching
|
||||
sources fail;
|
||||
- `Nearby devices are running a different Lanspread version` when mDNS sees an
|
||||
incompatible protocol; and
|
||||
- a non-blocking networking diagnostic if the installation identity cannot be
|
||||
@@ -484,18 +483,18 @@ version mismatch rather than failing silently.
|
||||
Keep the change inside existing crates unless implementation pressure proves a
|
||||
real reusable boundary; a new identity crate is not required by the design.
|
||||
|
||||
| Area | Responsibility |
|
||||
|---|---|
|
||||
| `lanspread-db` / `lanspread-compat` | Catalog content-manifest types and loading beside `game.db`. |
|
||||
| `lanspread-proto` | `PeerId`, `PeerEndpoint`, `content_id`, pull snapshots, change hints, author-owned Call-to-Play wire types, and the one protocol version. No crypto or storage logic. |
|
||||
| `lanspread-peer::identity` | Simple key/certificate load-or-generate, SPKI-derived ID, and test identity injection. |
|
||||
| `lanspread-peer::network` | Per-endpoint rustls client config, full responder verification, ALPN, and no address-only connect. |
|
||||
| discovery/handshake/liveness | Candidate-only mDNS, pinned pulls, hint coalescing, revision reconciliation on ping, endpoint generations, and version-mismatch reporting. |
|
||||
| `peer_db` | Authenticated endpoint/state records and exact `content_id` source lookup. |
|
||||
| download/storage/stream install | Validated catalog plan, hash-as-received, source quarantine/retry, sentinel commit, and protected staging. |
|
||||
| `call_to_play` | Local author slice, per-peer replacement snapshots, simple authority checks, timers, and bounds. |
|
||||
| Tauri/frontend | Global sharing switch, verification/progress failures, incompatible-version notice, and replacement of the full derived Call-to-Play view. |
|
||||
| peer CLI | Distinct deterministic identities, hostile TLS/content modes, and zero-prompt multi-peer scenarios. |
|
||||
| Area | Responsibility |
|
||||
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `lanspread-db` / `lanspread-compat` | Catalog content-manifest types and loading beside `game.db`. |
|
||||
| `lanspread-proto` | `PeerId`, `PeerEndpoint`, `content_id`, pull snapshots, change hints, author-owned Call-to-Play wire types, and the one protocol version. No crypto or storage logic. |
|
||||
| `lanspread-peer::identity` | Simple key/certificate load-or-generate, SPKI-derived ID, and test identity injection. |
|
||||
| `lanspread-peer::network` | Per-endpoint rustls client config, full responder verification, ALPN, and no address-only connect. |
|
||||
| discovery/handshake/liveness | Candidate-only mDNS, pinned pulls, hint coalescing, revision reconciliation on ping, endpoint generations, and version-mismatch reporting. |
|
||||
| `peer_db` | Authenticated endpoint/state records and exact `content_id` source lookup. |
|
||||
| download/storage/stream install | Validated catalog plan, hash-as-received, source quarantine/retry, sentinel commit, and protected staging. |
|
||||
| `call_to_play` | Local author slice, per-peer replacement snapshots, simple authority checks, timers, and bounds. |
|
||||
| Tauri/frontend | Global sharing switch, verification/progress failures, incompatible-version notice, and replacement of the full derived Call-to-Play view. |
|
||||
| peer CLI | Distinct deterministic identities, hostile TLS/content modes, and zero-prompt multi-peer scenarios. |
|
||||
|
||||
## 6. Implementation phases and gates
|
||||
|
||||
@@ -521,8 +520,8 @@ reported as Windows proof.
|
||||
Add the reproducible content-manifest generator and fixture manifests. Freeze
|
||||
the versioned manifest/content-ID encoding with golden tests. Extend local
|
||||
catalog state, build download plans only from that state, implement streaming
|
||||
BLAKE3 checks and source quarantine, and implement verified extracted
|
||||
manifests for Stream Install.
|
||||
BLAKE3 checks and source quarantine, and implement verified extracted manifests
|
||||
for Stream Install.
|
||||
|
||||
Do not claim completion from test fixtures alone: production catalog packages
|
||||
must have independently generated manifests, and the release/build path must
|
||||
@@ -542,10 +541,10 @@ generation-conditional. No trust database or identity UI is introduced.
|
||||
|
||||
### Phase 4 — make the single wire cutover
|
||||
|
||||
Bump the current protocol once and activate all coupled wire behavior from
|
||||
§4: pinned transport, catalog `content_id`, catalog-driven downloads, pull-only
|
||||
library synchronization, bounded invalidation hints, author-owned
|
||||
Call-to-Play snapshots, and no `Goodbye`.
|
||||
Bump the current protocol once and activate all coupled wire behavior from §4:
|
||||
pinned transport, catalog `content_id`, catalog-driven downloads, pull-only
|
||||
library synchronization, bounded invalidation hints, author-owned Call-to-Play
|
||||
snapshots, and no `Goodbye`.
|
||||
|
||||
This phase is not complete until:
|
||||
|
||||
@@ -576,11 +575,11 @@ phase; do not leave the shared-certificate or relayed-event description behind.
|
||||
|
||||
### Phase 5 — finish the small user-facing surface and audit
|
||||
|
||||
Add the global sharing switch and the concrete progress/error states from
|
||||
§3.6. Run a first-run test with an empty app-data directory, a normal restart,
|
||||
a corrupt identity file, and unwritable identity persistence; none may produce
|
||||
a key-management workflow or prevent the ephemeral fallback from participating
|
||||
for that run.
|
||||
Add the global sharing switch and the concrete progress/error states from §3.6.
|
||||
Run a first-run test with an empty app-data directory, a normal restart, a
|
||||
corrupt identity file, and unwritable identity persistence; none may produce a
|
||||
key-management workflow or prevent the ephemeral fallback from participating for
|
||||
that run.
|
||||
|
||||
Run all standard checks, the complete peer-CLI suite, fresh three-peer manual
|
||||
scenarios, production builds/bundles on supported platforms, and a final audit
|
||||
@@ -600,8 +599,8 @@ for:
|
||||
|
||||
## 7. Success criteria
|
||||
|
||||
The plan is complete when the following statement is true from a user's point
|
||||
of view:
|
||||
The plan is complete when the following statement is true from a user's point of
|
||||
view:
|
||||
|
||||
> I opened Lanspread at a LAN party, immediately saw the people and games
|
||||
> nearby, downloaded from all matching peers without approving devices, and
|
||||
@@ -610,6 +609,5 @@ of view:
|
||||
|
||||
From the implementation point of view, that experience rests on only three
|
||||
security boundaries: confined local paths, catalog-owned content hashes, and
|
||||
responder-pinned TLS. Call to Play deliberately reuses the pinned-pull model
|
||||
and remains ephemeral instead of becoming a second distributed security
|
||||
protocol.
|
||||
responder-pinned TLS. Call to Play deliberately reuses the pinned-pull model and
|
||||
remains ephemeral instead of becoming a second distributed security protocol.
|
||||
|
||||
Reference in New Issue
Block a user