docs(security): record final review and retained risks
Reassess the Fable ignored-finding ledger against the LAN-party threat model, move the newly bounded findings to fixed, and state the remaining S33, anonymous-membership, unrar, local-race, and elevated-script decisions precisely. Add the 2026-09-12 scan report with dependency results, independent follow-up review, validation evidence, and platform limits; update architecture limits to match the implementation. Test Plan: - just fmt - just clippy - just test - just frontend-test - just build-fixture - just peer-cli-build - just mdns-vendor-test - just security-audit - git diff --check
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
# Security findings not fixed (Fable 5.1 pass, 2026-09-02)
|
||||
# Retained security decisions and residual risks
|
||||
|
||||
Reassessed after the 2026-09-12 security pass.
|
||||
|
||||
This file records every finding from
|
||||
`SECURITY_AUDIT_2026-08-28_GEMINI-3.7-HIGH_TEAMWORK.md` and
|
||||
@@ -11,11 +13,11 @@ bundled catalog's BLAKE3 authority, and a hostile participant can be unplugged.
|
||||
Findings that only make sense against an internet-facing, multi-tenant threat
|
||||
model are noted as such.
|
||||
|
||||
Fixed findings are documented in the individual commits on this branch and are
|
||||
not repeated here. After a cross-review of the three parallel fix branches,
|
||||
NET-05, Codex [7] and Codex [12] were ported from the second branch and moved
|
||||
from "not fixed" to "fixed" (see the commits touching `justfile`,
|
||||
`state_paths.rs` and the bounded wire collections in `lanspread-proto`).
|
||||
The reassessment found that several availability findings had been dismissed too
|
||||
aggressively. They are now fixed with finite aggregate, per-origin, retry, and
|
||||
UI publication budgets. Entries retained below require trusted catalog input, an
|
||||
explicit local operation, or a same-machine writer; they are not remote LAN-peer
|
||||
integrity bypasses under this product model.
|
||||
|
||||
## Gemini audit
|
||||
|
||||
@@ -40,59 +42,60 @@ By design; see NET-01.
|
||||
### EXP2-SEC-01 — Post-unpack manifest verification (partially fixed)
|
||||
|
||||
- Fixed: symlink/reparse-point audit before promotion, plus `unrar -ol-`.
|
||||
- Not fixed: hashing every extracted file against the catalog manifest. The
|
||||
`.eti` archives are BLAKE3-verified at download time, so their extracted
|
||||
output is only as trustworthy as the catalog operator, who is trusted by
|
||||
definition. Stream Install already verifies extracted output because it
|
||||
receives extracted bytes from a peer; the ordinary path extracts locally from
|
||||
verified archives.
|
||||
- Retained: hashing every ordinary extracted file against the catalog manifest.
|
||||
Ordinary Install intentionally consumes every current regular root `.eti`;
|
||||
acceptance scenario S33 replaces a downloaded archive and requires those new
|
||||
local bytes to be installed. Stream Install remains exact: it verifies every
|
||||
path, kind, size, and BLAKE3 digest before promotion.
|
||||
|
||||
### EXP2-SEC-02 — RAR bomb / disk exhaustion
|
||||
|
||||
Same reasoning: a decompression bomb would have to be published in the catalog.
|
||||
Games are large by nature (the audit's own numbers are hundreds of gigabytes for
|
||||
a party catalog), and a preflight would need a new dependency for free-space
|
||||
queries plus a per-game expected size that the ordinary manifest does not carry.
|
||||
Disk-full during extraction already fails the transaction and rolls staging
|
||||
back.
|
||||
Ordinary archives may be locally replaced or added for S33, so a bomb need not
|
||||
come from the catalog. The retained boundary is an explicitly selected local
|
||||
root plus a user-started install. Games are large by nature and the ordinary
|
||||
manifest does not define expected expanded bytes. Disk-full or extractor failure
|
||||
rolls the transaction back, but decompressed bytes, CPU time, and free space are
|
||||
not preflighted.
|
||||
|
||||
### EXP2-SEC-05 — Pre-verification chunk writes
|
||||
|
||||
Uncommitted downloads are never published: `version.ini` is written only after
|
||||
every chunk verifies, and the ownership journal plus recovery path handle a
|
||||
crash mid-download. Buffering 4 MiB per in-flight chunk in memory to avoid
|
||||
writing bytes that are about to be verified anyway would raise memory use for no
|
||||
integrity gain.
|
||||
crash mid-download. Ordinary chunks can be 128 MiB; buffering complete chunks in
|
||||
memory to avoid confined temporary writes would materially raise memory use
|
||||
without changing publication integrity.
|
||||
|
||||
### EXP2-SEC-07 — Ambient filesystem calls in `stream_install.rs`
|
||||
|
||||
Architectural parity request. The staging directory is created empty and owned
|
||||
by the transaction, every path is a validated `CanonicalCatalogPath` resolved by
|
||||
`validate_relative_path` (which now applies the full portable component rules
|
||||
and rejects symlink escapes). Refactoring the receiver onto capability handles
|
||||
is a large change with no concrete exploit behind it.
|
||||
Remote paths, kinds, sizes, and digests are catalog-owned, and the completed
|
||||
staging tree is audited before transactional promotion. The receiver still uses
|
||||
ambient path calls inside its owned staging directory. Exploiting that gap
|
||||
requires a concurrent same-machine writer able to mutate the selected root; it
|
||||
is retained as defense-in-depth work rather than a remote peer bypass.
|
||||
|
||||
### SEC-IPC-01 — Elevated execution of game scripts (partially fixed)
|
||||
|
||||
- Fixed: `sanitize_username` now also strips `& | < > ^` so a batch script's
|
||||
textual `%~4` expansion cannot chain commands.
|
||||
- Not fixed: running the scripts elevated, and re-hashing them at launch time.
|
||||
Elevated setup is the feature: the shared games need registry entries,
|
||||
redistributables and firewall rules installed. The scripts ship inside catalog
|
||||
archives that are BLAKE3-verified before installation, so "untrusted P2P batch
|
||||
scripts" mischaracterises them. Re-verifying the file at launch would only
|
||||
defend against a local actor who can already write to the user's own game
|
||||
directory, i.e. who already runs code as that user.
|
||||
- Fixed: launch-time trust binding. A fixed-role elevated worker reloads the
|
||||
bundled authority, matches the embedded `ContentId`, verifies exact size and
|
||||
BLAKE3 from a no-follow locked handle, resolves System32 `cmd.exe`, and keeps
|
||||
path locks alive in the command process. Changed, unmanifested,
|
||||
reparse-backed, markerless, and streamed-only scripts fail closed.
|
||||
- Retained: all three scripts still run elevated. This is required for registry
|
||||
entries, redistributables, and firewall rules. Native Windows lock-transfer
|
||||
behavior is not proven in this Linux checkout, and a trusted script may still
|
||||
invoke mutable secondary executables or configuration.
|
||||
- The proposed `[A-Za-z0-9_-]` username allowlist was rejected because it would
|
||||
mangle ordinary names (spaces, umlauts).
|
||||
|
||||
### SEC-IPC-04 — Sandboxing `unrar` (partially fixed)
|
||||
|
||||
- Fixed: `-ol-` and the post-extraction link audit.
|
||||
- Not fixed: Landlock/AppContainer style OS sandboxing of the sidecar. The input
|
||||
is a catalog-verified archive; a process sandbox for it is not proportionate
|
||||
to a LAN launcher. Note that the `-sl-` flag the audit recommends does not
|
||||
exist (`-sl<size>` is a size filter).
|
||||
- Not fixed: Landlock/AppContainer style OS sandboxing of the sidecar. Ordinary
|
||||
S33 input is not necessarily catalog-identical, but it is selected local input
|
||||
rather than peer-controlled bytes. `unrar` runs with `-ol-`, bounded output
|
||||
capture and cancellation cleanup, a staging link audit, and bounded remote
|
||||
Stream Install provider admission. Note that the `-sl-` flag the audit
|
||||
recommends does not exist (`-sl<size>` is a size filter).
|
||||
|
||||
### SEC-IPC-05 — `allow-create-webview-window` capability
|
||||
|
||||
@@ -109,63 +112,80 @@ for this app.
|
||||
|
||||
## Codex scan (`security-report/report.md` numbering)
|
||||
|
||||
### [1] Anonymous LAN requesters can monopolize global pools
|
||||
### [1] Anonymous LAN requesters can monopolize global pools (fixed 2026-09-12)
|
||||
|
||||
Global pools (64 connections, 64 control streams, 48 bulk transfers) are the
|
||||
intended fairness model for a room of peers; per-origin quotas conflict with the
|
||||
anonymous requester design, and legitimate use is bursty (ten people pulling the
|
||||
newest game from one seeder). Discovery now has a per-source-IP budget, which
|
||||
covers the cheapest flood. A participant who saturates a seeder at a LAN party
|
||||
is visible and can be dealt with in person.
|
||||
Requester anonymity remains, but resource anonymity does not. One observed IP
|
||||
may start 8 handshakes per 3 seconds and retain at most 8 connections, 8 of 16
|
||||
control tasks, 8 of 48 bulk transfers, 1 of 2 Stream Install providers, and 8
|
||||
MiB of the shared 32 MiB response buffer. Raw and Stream Install sends have
|
||||
absolute deadlines, so incremental progress cannot renew those slots
|
||||
indefinitely.
|
||||
|
||||
### [3] Ordinary install extracts uncatalogued root archives (partially fixed)
|
||||
|
||||
- Fixed: link audit before promotion.
|
||||
- Not fixed: restricting extraction to the catalog's archive set and verifying
|
||||
extracted output. The scenario requires the local user to place an extra
|
||||
`.eti` in their own game directory. See EXP2-SEC-01.
|
||||
extracted output. This is the behavior exercised by S33: the local user
|
||||
replaces or adds `.eti` files and expects Ordinary Install to consume the
|
||||
current set. See EXP2-SEC-01.
|
||||
|
||||
### [4] Sybil sources can multiply one chunk deadline into a multi-hour loop
|
||||
### [4] Sybil retry amplification (fixed)
|
||||
|
||||
The retry loop tries each distinct peer identity once per chunk by design,
|
||||
cancellation is always available, and a real party LAN has a handful of peers,
|
||||
not 64 hostile identities. A global attempt budget would make large legitimate
|
||||
downloads give up earlier under flaky Wi-Fi.
|
||||
One failed chunk may try at most eight distinct peer IDs at distinct endpoint
|
||||
IPs. All retries share one nonrenewable 20-minute window created after the
|
||||
initial failure; each in-flight attempt retains the earlier ten-minute limit.
|
||||
Stream Install separately permits four endpoint IPs under one catalog-sized
|
||||
total deadline.
|
||||
|
||||
### [5] One peer can force continuous rendering of thousands of active calls
|
||||
### [5] Unbounded active-call rendering (fixed)
|
||||
|
||||
The 4096-event author bound is a wire bound; a peer that publishes thousands of
|
||||
simultaneous calls degrades the ticker for everyone but this is a prank that the
|
||||
room can see and stop. Semantic caps on open calls per author would be a product
|
||||
decision (how many calls may one person host?) rather than a security fix.
|
||||
One remote author may retain at most 128 creator roots. Frontend projection is
|
||||
limited to 128 nominations and the latest 256 messages, and messages are sorted
|
||||
once rather than after every append.
|
||||
|
||||
### [8] Sybil peers can exhaust aggregate state and unbounded UI publication
|
||||
### [8] Aggregate state and UI publication (fixed)
|
||||
|
||||
Worst case is 64 authenticated identities each holding a 4 MiB author slice,
|
||||
i.e. a few hundred MiB retained by a desktop process, and only while a hostile
|
||||
host keeps dozens of identities alive on the party LAN. Aggregate byte budgets
|
||||
and bounded latest-value UI channels are a redesign of the state layer, not a
|
||||
fix, and are not warranted by this deployment model.
|
||||
Committed state now has eight identities per endpoint IP, 16,384 aggregate
|
||||
library rows, and 16,384 aggregate remote Call-to-Play events. Rejected
|
||||
revisions retain watermarks instead of being pulled repeatedly. Each heavy UI
|
||||
view keeps one queued snapshot and one replaceable pending snapshot while small
|
||||
lifecycle events remain lossless and FIFO.
|
||||
|
||||
### [10] Mutable game scripts are launched elevated without trust binding
|
||||
### [10] Elevated mutable-script trust binding (fixed)
|
||||
|
||||
See SEC-IPC-01.
|
||||
See SEC-IPC-01. Required elevation remains; mutable-path trust binding does not.
|
||||
|
||||
### [11] Selected game directories can trigger unbounded monitoring work
|
||||
### [11] Unbounded selected-root monitoring (fixed)
|
||||
|
||||
The monitored directory is the one the user selected on their own machine.
|
||||
Budgeting traversal of the user's own files against the user is out of scope.
|
||||
Index reads, games, archive fingerprints, recursive entries, depth, bytes,
|
||||
elapsed scan time, `version.ini`, launch-settings traversal, and INI reads now
|
||||
have explicit per-file and aggregate ceilings. Monitor failures retain the
|
||||
previous complete snapshot and back off exponentially per selected root.
|
||||
|
||||
### [13] Catalog preflight can read outside the package root
|
||||
### [13] Catalog preflight can read outside the package root (fixed 2026-09-12)
|
||||
|
||||
Publisher tooling run by the catalog operator over their own package tree; the
|
||||
disclosed bytes are a bounded, trimmed `version.ini` echoed in the operator's
|
||||
own error message.
|
||||
Full-selection preflight now rejects a linked or non-directory package root
|
||||
before the bounded, non-link `version.ini` read, and mismatch diagnostics omit
|
||||
package-controlled observed contents.
|
||||
|
||||
## Summary
|
||||
|
||||
| Status | Findings |
|
||||
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Fixed | NET-02, NET-03, NET-04, NET-05, EXP2-SEC-03, EXP2-SEC-04, EXP2-SEC-06, SEC-IPC-02, SEC-IPC-03, SEC-DB-01, Codex [2], [6], [7], [9], [12], [14], [15] |
|
||||
| Partially fixed | NET-01, EXP2-SEC-01, SEC-IPC-01, SEC-IPC-04, Codex [3], [10] |
|
||||
| Not fixed | NET-06, EXP2-SEC-02, EXP2-SEC-05, EXP2-SEC-07, SEC-IPC-05, SEC-FE-01, Codex [1], [4], [5], [8], [11], [13] |
|
||||
| Status | Findings |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| Fixed | NET-02, NET-03, NET-04, NET-05, EXP2-SEC-03, EXP2-SEC-04, EXP2-SEC-06, SEC-IPC-02, SEC-IPC-03, SEC-DB-01, Codex [1], [2], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15] |
|
||||
| Partially fixed | NET-01, EXP2-SEC-01, SEC-IPC-01, SEC-IPC-04, Codex [3] |
|
||||
| Accepted/retained | NET-06, EXP2-SEC-02, EXP2-SEC-05, EXP2-SEC-07, SEC-IPC-05, SEC-FE-01 |
|
||||
|
||||
The retained items have these practical boundaries:
|
||||
|
||||
| Retained risk | Boundary |
|
||||
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Anonymous membership and metadata/content access | Intended LAN behavior; responder pinning, exact catalog identity, and active per-origin quotas protect integrity and availability. |
|
||||
| Ordinary archive and extracted-output authority | Explicit local install and S33 mutation; links are blocked, but the current archive set, expanded bytes, and decompression cost are local user authority. |
|
||||
| Pre-verification temporary writes | Confined ownership journals and a final sentinel prevent publication; failed chunks can still consume bounded temporary disk space. |
|
||||
| Ambient Stream Install staging calls | Remote paths, sizes, and digests are exact; a concurrent same-machine root mutation remains the escape precondition. |
|
||||
| Elevated execution | Only exact catalog-authorized scripts launch, but those trusted scripts intentionally receive administrator authority and may invoke mutable dependencies. |
|
||||
| External `unrar` | No OS sandbox; local mutated archives remain user-level extractor input. |
|
||||
| Main-window companion creation | Used for fixed local log windows; CSP and no known renderer injection path limit reachability. |
|
||||
| User regex CPU | The pattern is local input; remote-controlled log lines can only supply the haystack. |
|
||||
| Distributed saturation | One IP cannot monopolize active pools; cooperating hosts on several IPs can still reach the finite global limits. |
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
# Security review — 2026-09-12
|
||||
|
||||
## Scope and threat boundary
|
||||
|
||||
This pass reviewed the full Lanspread workspace after the Fable 5.1 remediation.
|
||||
The product is a desktop launcher for LAN parties, not an Internet-facing
|
||||
multi-tenant service. Remote LAN peers and their network traffic are untrusted.
|
||||
The bundled catalog is trusted authority. A user-selected local game root and
|
||||
explicit local install actions remain user authority. The three Windows launch
|
||||
scripts intentionally run elevated.
|
||||
|
||||
## Reassessment of previously ignored findings
|
||||
|
||||
The earlier file dismissed several finite-resource findings too broadly. This
|
||||
pass fixed anonymous pool monopolization, retry multiplication, oversized remote
|
||||
state, heavy UI publication, selected-root ingestion, elevated-script trust
|
||||
binding, and catalog-preflight traversal. The remaining accepted decisions and
|
||||
their exact limits are recorded in `SECURITY_FIXES_FABLE5.1_IGNORED.md`.
|
||||
|
||||
The main retained decisions are:
|
||||
|
||||
- anonymous LAN membership, metadata browsing, and catalog-content downloads;
|
||||
- Ordinary Install consuming the current root `.eti` set, including the S33
|
||||
local-mutation workflow;
|
||||
- transactional pre-verification disk writes;
|
||||
- no OS sandbox around `unrar`;
|
||||
- local-user regex CPU cost and the fixed companion-window capability; and
|
||||
- required elevation for catalog-authorized setup, game, and server scripts.
|
||||
|
||||
These are proportionate for the application only because remote paths and
|
||||
content remain catalog-bound, local mutations require an explicit local action,
|
||||
and remote work now has finite global and per-origin budgets.
|
||||
|
||||
## Findings fixed in this pass
|
||||
|
||||
| Finding | Final boundary |
|
||||
| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| mDNS advertised-address quota bypass | Candidate quotas use the observed response IP. One source gets 8 active-or-cooling candidates. |
|
||||
| Unbounded mDNS dependency state | Cache records are capped at 1,024 globally and 128 per source; timers are deduplicated and capped at 4,096. |
|
||||
| Anonymous request pool monopolization | One IP gets 8 handshake attempts per 3 seconds, 8/64 connections, 8/16 control tasks, 8/48 bulk tasks, and 1/2 Stream Install providers. |
|
||||
| Renewable bulk-send deadlines | Raw chunks use one absolute ten-minute window; Stream Install uses a catalog-sized absolute window plus ten-minute inactivity through FIN. |
|
||||
| Ordinary Sybil retry multiplication | At most 8 distinct identities at distinct IPs share one 20-minute retry window after the initial failure. |
|
||||
| Stream Install drip and retry multiplication | At most 4 endpoint IPs share `10 min + 2 x catalog bytes / 1 MiB/s`; only written nonempty file bytes refresh inactivity. |
|
||||
| Eager control-response allocation | An 8 MiB endpoint-IP budget and shared 32 MiB connector budget are acquired after manual four-byte length validation and held through decode. |
|
||||
| Aggregate remote state | At most 16,384 remote library rows and 16,384 remote Call-to-Play events; rejected revisions retain watermarks. |
|
||||
| Peer identity amplification | At most 8 authenticated identities may reside at one endpoint IP, within the global 64-peer limit. |
|
||||
| Unbounded full-view publication | Remote-library and Call-to-Play views keep one queued and one replaceable pending snapshot; lifecycle events remain FIFO. |
|
||||
| Call-to-Play projection cost | One author retains 128 creator roots; the UI projects 128 nominations and 256 messages and sorts messages once. |
|
||||
| Selected-root ingestion | Index, traversal, depth, byte, elapsed-time, bounded `version.ini`, and 64 MiB aggregate settings-read limits fail without publishing partial state. |
|
||||
| Mutable elevated Windows scripts | A fixed-role elevated worker reloads embedded catalog authority, verifies the locked script, uses System32 `cmd.exe`, and transfers locks into the child. |
|
||||
| Catalog preflight root traversal | Package roots must be regular non-link directories before a bounded leaf read; diagnostics omit observed package contents. |
|
||||
| Developer recipe shell injection | Just positional arguments and exported values are passed as quoted shell data. |
|
||||
| Unused frontend shell authority | The main-window `shell:allow-open` capability was removed. |
|
||||
|
||||
## Independent follow-up review
|
||||
|
||||
A fresh source-to-sink review found and closed six additional gaps: response
|
||||
semaphore head-of-line blocking, half-open handshake fairness, an unbounded
|
||||
public readiness-sentinel read, aggregate settings-file reads, repeated monitor
|
||||
backoff reset after a root switch, and an over-strict ten-minute provider egress
|
||||
deadline. The corrected provider deadline is ten minutes plus one catalog copy
|
||||
at 1 MiB/s. The handshake limiter drops excess Initials because issuing a Retry
|
||||
token would bypass s2n's next connection-attempt callback.
|
||||
|
||||
The reviewer read back the follow-up range and found no remaining concrete
|
||||
source-level bypass or regression.
|
||||
|
||||
## Dependency scan
|
||||
|
||||
`just security-audit` refreshed RustSec and scanned 578 locked Rust packages,
|
||||
then audited the Deno lockfile. It found no known vulnerabilities in either
|
||||
graph. RustSec reported seven informational warnings:
|
||||
|
||||
- six unmaintained build/transitive crates (`proc-macro-error` and five `unic-*`
|
||||
crates); and
|
||||
- `glib 0.18.5` unsoundness limited to `VariantStrIter` methods.
|
||||
|
||||
The affected `glib` API has no call site in Lanspread or its locked dependants,
|
||||
and the GTK 0.18 branch is constrained by the current Tauri Linux backend. The
|
||||
warnings are dependency maintenance work rather than reachable findings.
|
||||
|
||||
## Residual limits
|
||||
|
||||
- Native Windows execution was unavailable. Linux-visible worker authority,
|
||||
parsing, digest, role, and handoff tests pass, but the Windows lock-transfer
|
||||
test has not run.
|
||||
- The absent production scripts may invoke mutable secondary executables or
|
||||
configuration as administrator. The production payload must be audited before
|
||||
claiming transitive administrator-code provenance.
|
||||
- Ordinary Install deliberately accepts locally changed or additional `.eti`
|
||||
archives. Their expanded output and decompression cost are not catalog
|
||||
authority; links are skipped and audited before promotion.
|
||||
- Stream Install staging still uses ambient path calls. Exploitation requires a
|
||||
concurrent same-machine writer able to mutate the selected root.
|
||||
- Per-origin fairness cannot stop cooperating hosts or one host using several
|
||||
usable IP addresses; finite global limits remain authoritative.
|
||||
|
||||
## Validation
|
||||
|
||||
- `just fmt`: passed.
|
||||
- `just clippy`: passed with warnings denied.
|
||||
- `just test`: passed, including 541 peer tests and all workspace targets.
|
||||
- `just frontend-test`: 94 tests passed.
|
||||
- `just build-fixture`: passed.
|
||||
- `just peer-cli-build`: passed.
|
||||
- `just peer-cli-tests`: all four catalog profiles passed; the container
|
||||
scenarios could not start because this host denies Podman's `newuidmap`
|
||||
capability, including outside the sandbox.
|
||||
- `just mdns-vendor-test`: 106 tests passed with socket access.
|
||||
- `just security-audit`: no known Rust or Deno vulnerabilities; seven accepted
|
||||
RustSec informational warnings.
|
||||
|
||||
Physical-LAN multicast behavior and native Windows execution remain runtime
|
||||
validation boundaries.
|
||||
@@ -60,9 +60,12 @@ When a peer is discovered:
|
||||
emit UI membership events. Advertised addresses must be unicast with a
|
||||
nonzero port; multicast, broadcast, and unspecified targets are dropped
|
||||
before any handshake packet is sent. The mDNS ingress queue and active
|
||||
candidate negotiations are each capped at 64, and one source IP may hold at
|
||||
most 8 active-or-cooling candidates so a single host cannot fill every slot
|
||||
by rotating claimed peer IDs and ports.
|
||||
candidate negotiations are each capped at 64, and one observed response IP
|
||||
may hold at most 8 active-or-cooling candidates. The advertised A/AAAA target
|
||||
is never used as provenance, so one host cannot escape the budget by rotating
|
||||
target addresses, peer IDs, or ports. The vendored mDNS daemon also caps
|
||||
cached records at 1,024 globally and 128 per response IP, with at most 4,096
|
||||
deduplicated timers.
|
||||
2. Reserve a candidate negotiation lease before queueing or awaiting work, then
|
||||
establish a TLS-pinned connection to that exact endpoint. Missing or non-v8
|
||||
records are rejected, and neither an ephemeral QUIC source port nor a payload
|
||||
@@ -103,14 +106,20 @@ When a peer is discovered:
|
||||
work for five seconds, and runs at most eight pinned pulls and eight hint
|
||||
sends concurrently. Child work is drained lexically on shutdown.
|
||||
- The server admits at most 64 unauthenticated handshakes, 64 established
|
||||
connection scopes, and 32 control streams per connection. Each control stream
|
||||
accepts one frame followed by request EOF and sends at most one response.
|
||||
Inbound request frames are capped at 64 KiB (the QUIC receive window per
|
||||
server stream matches it), response frames at 8 MiB, and control I/O has
|
||||
ten-second deadlines. Wire collections (library games, Call to Play author
|
||||
events) are deserialized through a bounded visitor that materializes at most
|
||||
one element past the semantic limit and discards the rest, so an oversize
|
||||
frame cannot claim more memory than a valid one before validation rejects it.
|
||||
connection scopes, and 32 control streams per connection. One observed IP may
|
||||
start at most 8 handshakes during one 3-second handshake window, before TLS
|
||||
and established-connection leases exist; the source ledger itself holds at
|
||||
most 256 IPs. One observed IP may retain at most 8 connections, 8 of the 16
|
||||
global control tasks, 8 of the 48 global bulk tasks, and 1 of the 2 Stream
|
||||
Install providers. Each control stream accepts one frame followed by request
|
||||
EOF and sends at most one response. Inbound request frames are capped at 64
|
||||
KiB (the QUIC receive window per server stream matches it), response frames at
|
||||
8 MiB, and control I/O has ten-second deadlines. Response buffering permits at
|
||||
most 8 MiB per endpoint IP within one shared 32 MiB pool. Wire collections
|
||||
(library games, Call to Play author events) are deserialized through a bounded
|
||||
visitor that materializes at most one element past the semantic limit and
|
||||
discards the rest, so an oversize frame cannot claim more memory than a valid
|
||||
one before validation rejects it.
|
||||
|
||||
### Call to Play replication
|
||||
|
||||
@@ -215,6 +224,10 @@ pull.
|
||||
- presence of `local/` as a directory
|
||||
- If the fingerprint is unchanged, reuse the cached local summary.
|
||||
- Only run a recursive scan for new or changed games.
|
||||
- Snapshot and recursive work has explicit entry, game, depth, byte, file-size,
|
||||
and elapsed-time ceilings. Failure retains the previous complete state. Poll
|
||||
failures back off from one second to five minutes per attempted root; a root
|
||||
switch resets that delay once.
|
||||
|
||||
## Local State and Recovery
|
||||
|
||||
@@ -231,6 +244,9 @@ Downloaded and installed are independent predicates:
|
||||
- Install and update transactions unpack into staging, then overwrite the first
|
||||
discovered game-provided `account_name.txt` and `language.txt` files under the
|
||||
staged tree from launcher settings before promoting it to `local/`.
|
||||
- Launch-settings discovery uses one bounded traversal, reads at most 1 MiB per
|
||||
INI and 64 MiB in aggregate under the same ten-minute clock, and finishes all
|
||||
candidate reads before its first write or marker publication.
|
||||
|
||||
Reserved per-game paths:
|
||||
|
||||
@@ -325,8 +341,10 @@ Most scans become O(number of game dirs), with full recursion only when needed.
|
||||
open/request/receive/check lifecycle of each ordinary chunk.
|
||||
- An integrity failure quarantines `(PeerId, content_id)` in shared memory for
|
||||
the rest of that peer runtime, independent of address changes. Transport
|
||||
failures remain retryable without quarantine. Each failed chunk may try every
|
||||
distinct eligible peer identity once; there is no separate numeric retry cap.
|
||||
failures remain retryable without quarantine. Each failed chunk may try at
|
||||
most eight distinct authenticated identities at distinct endpoint IPs. All
|
||||
retries share one nonrenewable 20-minute window created after the initial
|
||||
failure; an individual attempt still has the earlier ten-minute limit.
|
||||
- A no-transfer local shortcut is allowed only when settled ownership records
|
||||
the exact expected committed content ID. A matching `version.ini` alone, or a
|
||||
legacy/pre-content-ID record, is not catalog-content proof.
|
||||
@@ -343,9 +361,10 @@ Most scans become O(number of game dirs), with full recursion only when needed.
|
||||
|
||||
### Ordinary install extraction
|
||||
|
||||
- `install`/`update` extract every direct regular root `.eti` archive with the
|
||||
external `unrar` unpacker into the staging directory. The archives were
|
||||
BLAKE3-verified against the catalog when downloaded.
|
||||
- `install`/`update` extract every current direct regular root `.eti` archive
|
||||
with the external `unrar` unpacker into the staging directory. Downloaded
|
||||
archives were BLAKE3-verified, but the ordinary path deliberately accepts
|
||||
later local replacements and extra archives for the S33 workflow.
|
||||
- `unrar` runs with `-ol-`, so symbolic-link entries are skipped, and the
|
||||
staging tree is walked without following links before promotion. Any symlink
|
||||
(or reparse point on Windows) aborts the transaction and rolls staging back,
|
||||
@@ -359,6 +378,9 @@ Most scans become O(number of game dirs), with full recursion only when needed.
|
||||
output and only when its direct regular root `.eti` set exactly equals the
|
||||
catalog archive set. Missing or extra archives stop before the extraction
|
||||
provider receives authority.
|
||||
- Provider egress has one nonrenewable deadline of ten minutes plus the exact
|
||||
catalog streamed bytes at 1 MiB/s, so large valid games are finite without a
|
||||
fixed ten-minute whole-transfer cutoff.
|
||||
- The Stream Install request names the exact catalog `ContentId`. All
|
||||
path-bearing frames, including archive names, use `CanonicalCatalogPath` and
|
||||
the fallible checked decoder distinguishes malformed framing from an explicit
|
||||
@@ -371,8 +393,12 @@ Most scans become O(number of game dirs), with full recursion only when needed.
|
||||
early corruption signal, not an authority boundary.
|
||||
- Integrity failures quarantine the source for that catalog content and retry a
|
||||
fresh transaction from another eligible peer. Transport failures retry without
|
||||
quarantine; local I/O and cancellation stop. Every failed attempt rolls back
|
||||
before the next source begins.
|
||||
quarantine; local I/O and cancellation stop. At most four distinct endpoint
|
||||
IPs are attempted. Every attempt shares a catalog-sized total deadline of ten
|
||||
minutes plus two copies of the expected streamed bytes at 1 MiB/s. The
|
||||
ten-minute inactivity timer refreshes only after a nonempty, catalog-checked
|
||||
file chunk is written. Every failed attempt rolls back before the next source
|
||||
begins.
|
||||
- After complete catalog verification, account/language/persona settings are
|
||||
rewritten in staging before promotion. The one-shot launch-settings marker is
|
||||
written only after successful promotion; if it cannot be written, first play
|
||||
|
||||
Reference in New Issue
Block a user