# nigig-traffic threat model (TRAFFIC-13, scoped) Traffic is an OFFLINE, non-authenticated app. There is deliberately NO network, payment, account, telemetry, or instructor-sharing boundary in this tranche — and no such claims may appear in copy (ADR 001). Do not add web-style encryption/authentication work here. Real surfaces: ## 1. Dependency / toolchain supply chain - Pinned git dependency (`andodeki/makepad` fork) + crates.io packages. - Mutable CI bootstrap (`actions/checkout@v4`, rustup installer download). - Gates: `Cargo.lock` committed + `cargo metadata --locked` clean-tree check; advisory/license/duplicate/yanked/source policy (CI `supply-chain` job); third-party actions pinned to immutable SHAs; toolchain from controlled image or digest-verified installer; SBOM + license/source manifest archived per release (`docs/SBOM.md` at release). - Exceptions (advisory waivers, unlicensed crates) require owner, reason, and expiry, and fail closed when expired. ## 2. Scenario / profile / replay / asset parsing - Inputs: static catalog (code), future imported scenario packs, learner profile JSON, replay digests, GLB/image assets. - Policy: byte/count/depth/string/path limits BEFORE allocation/parse; reject absolute paths, `..` traversal, backslashes, unsupported file types; bounded, allocation-free-first parsers (`persistence.rs`, `assets.rs`); fuzz the byte-accepting parsers/converters with a corpus (`cargo fuzz` target or `#[test]` hostile-archive suite at release). - Oversize/decompression-bomb/corrupt inputs fail within memory/time budgets without panic (no `unwrap`/`expect` in `src/`; CI greps it). ## 3. Path and resource exhaustion - Asset IDs are logical (`sign_stop.glb`), never filesystem paths; one validated join root; no `CARGO_MANIFEST_DIR` probes in packaged builds. - Budgets (§6) enforced by validators: ≤256 entities/scenario, ≤32 movers, ≤2048 statics, ≤64 criteria, ≤4096 rule events (ring cap), 30-minute attempt cap, 8 MiB profile, 10k attempts before compaction, 150 MiB curated assets, texture residency caps. Over-budget content is excluded from the catalog, never silently downscaled for assessment. ## 4. Local learner privacy - Anonymous by default: opaque learner ID (`anon-*`), no names, no telemetry, no cloud sync, no home-grown at-rest encryption. - Restrictive platform file permissions on the profile dir; atomic temp + flush/sync + rename writes; verify-by-reopen; recovery states (Absent / Corrupt-preserved / FutureVersion-read-only / IoError) that never silently reset to empty. - Logs are bounded and contain no profile contents or absolute developer paths. Export/delete/reset controls ship with persistence before any "saved locally" copy appears (settings copy is session-only until then). - Explicit NON-GOALS this tranche: accounts, instructor sharing, cloud backup, analytics, secondary use. Each needs a separately reviewed purpose, retention policy, consent flow, and threat model. ## 5. Unsafe / dependency-code scope - Source-level `unsafe` grep covers Traffic `src/` only and does NOT constrain `unsafe` in dependencies (including the Makepad fork). Dependency risk is managed via pin + audit + SBOM, not grep. ## Residual risks (accepted, tracked) - GUI harness cannot boot in CI (TRAFFIC-14): runtime regressions rely on headless production-path journeys until the harness is repaired. - No qualified curriculum review (TRAFFIC-08): all content is practice preview; assessment release is blocked. - Pinned fork is internally inconsistent (TRAFFIC-P0-01): builds fail closed until a coherent upstream revision is pinned.