e78c58fc28
The distribution answer existed only in conversation, so every session re-derived it. It is now in the map, and the route decision is explicitly parked as the creator's. new TASKS/research-store-certification.md — Store Policies 7.20 + MSIX packaging: which policies bind, which don't (and why), cert economics, camera/mic gating layers, YouTube age + COPPA, the 11.12 UGC judgment call. Two real defects surfaced, neither fixed (docs-only unit): - FfmpegLocator downloads an unsigned exe from GitHub and runs it. That is policy 10.2.2 (dynamic code inclusion) verbatim, and it is the root cause of the 2026-09-01 404 — the pin aged out of BtbN's 14-day retention on the creator's first real recording attempt. -> TASK 48 item 1, not Store-conditional. - Distribution.md:318 recommended a $400+/yr EV cert for a SmartScreen bypass Microsoft removed in March 2024. Fixed; had it shipped it would have cost $400+/yr to buy what $150 buys. Also new: TASKS/task-48 (checklist, carved out of TASK 36 item 6) and TASKS/task-49 (chat profanity filter, not blocked). ai.md gains the durable invariants — full trust or recording breaks silently, chat is rendered never stored — plus a correction to the FFmpeg locator section. MyMistakes.md records the lesson: a policy citation is a claim about scope, not just text. MARCOM.md got the privacy-copy guard but is gitignored by design, so that edit stays local and did not travel here.
188 lines
9.2 KiB
Markdown
188 lines
9.2 KiB
Markdown
# TASK 48 — Distribution & packaging: route decision, MSIX, signing
|
||
|
||
> Catalog: [`TASKS.md`](../TASKS.md) — status and requirements live here.
|
||
> **Research: [`TASKS/research-store-certification.md`](research-store-certification.md)** — the "why".
|
||
> Carved out of TASK 36 item 6 (release engineering) on 2026-09-27 so distribution has one
|
||
> owner instead of three scattered mentions.
|
||
|
||
**Status:** ☐ Queued — **⏳ blocked on the creator's route decision** (item 0)
|
||
|
||
**Goal:** get LlamaCasty in front of a user without a SmartScreen scarewall, ideally without
|
||
an annual certificate bill, and without breaking recording, capture, or audio.
|
||
|
||
⚠️ **Nothing in this task is decided yet.** The research is done and MSIX is the
|
||
front-runner, but the route is the creator's call. Items 2–8 describe the MSIX path
|
||
*conditional on choosing it* — if the answer is Polar-hosted + a cert, most of them evaporate
|
||
and only items 0, 1, and 9 remain.
|
||
|
||
---
|
||
|
||
## 0. ⛔ THE DECISION — creator, not AI
|
||
|
||
Pick one:
|
||
|
||
| # | Route | Cert/yr | SmartScreen | Extra work |
|
||
|---|---|---|---|---|
|
||
| **A** | Store MSIX + Store IAP | **$0** | none | MSIX packaging; Store review; **Polar deleted**; revenue cut |
|
||
| **B** | Store MSIX + **Polar** | **$0** | none | MSIX packaging; Store review; two systems to maintain |
|
||
| **C** | **Polar file hosting** + Polar + own cert | $120–300 | warning ramp | none beyond buying the cert |
|
||
| **D** | Store EXE (10.2.9) | $120–300 | warning ramp | **dominated** — cert anyway + silent install + maintain our own URL |
|
||
|
||
**C is the status quo already sketched in `Distribution.md:14`/`:296`** — Polar hosts the
|
||
signed exe (10GB, signed personal URLs, SHA-256). The Store is not needed for delivery at
|
||
all; its only unique value is *free Microsoft signing with no warning*.
|
||
|
||
Cost table, full detail, and the dead-EV correction: `research-store-certification.md` §2–3.
|
||
|
||
---
|
||
|
||
## 1. ⛔ Bundle ffmpeg instead of downloading it — **do this on EVERY route**
|
||
|
||
**Not conditional on the Store. This is worth doing regardless.**
|
||
|
||
`Services/Encoder/FfmpegLocator.cs:37-38` pins a GitHub release URL; `LocateAsync`
|
||
downloads (line 69), extracts (line 73), and the encoder executes the result. On a cold
|
||
cache the app downloads an unsigned executable from the internet and runs it.
|
||
|
||
- **Store blocker:** policy 10.2.2 forbids dynamic code inclusion (download-and-execute).
|
||
- **Route-independent bug class:** `FfmpegLocator.cs:30-35` records that the previous
|
||
daily pin aged out of GitHub retention and **404'd on the first real recording attempt
|
||
(2026-09-01)**. Bundling kills this permanently and removes the startup network
|
||
dependency.
|
||
|
||
**Do:** ship `ffmpeg.exe` + `ffprobe.exe` + the `libav*.dll` family inside the package;
|
||
`FfmpegLocator` resolves PATH → package-local → cache, and never downloads.
|
||
`IFfmpegLocator`'s contract, the `ExtractBinaries` staging discipline, and the existing
|
||
`FfmpegLocatorTests` cold/cache/PATH coverage all still apply.
|
||
|
||
**Licensing:** the LGPL-shared build was chosen for LGPL §6 compliance (dynamic linking =
|
||
"license text + source offer"). That reasoning is **unaffected**. Confirm
|
||
`THIRD-PARTY-NOTICES.txt` covers the bundled build.
|
||
|
||
**Record the immutable tag** in `TASKS.md` per the licensing rule, and note the URL is now
|
||
a provenance record rather than a runtime fetch.
|
||
|
||
---
|
||
|
||
## 2. ☐ `Package.appxmanifest` — full trust, camera, microphone
|
||
|
||
Only if the Store is chosen. There is currently **no `.manifest` file in the repo at all**,
|
||
so this is purely additive.
|
||
|
||
- `rescap:Capability Name="runFullTrust"` — medium integrity, not AppContainer
|
||
- `webcam` and `microphone` capabilities
|
||
- `uap10:TrustLevel="mediumIL"`, `uap10:RuntimeBehavior="packagedClassicApp"`
|
||
- `uap10:RuntimeBehavior="packagedClassicApp"` is what allows launching package
|
||
executables as child processes
|
||
- Declare **English only** (10.7 — otherwise the description must be localized into every
|
||
declared language)
|
||
- Block map SHA2-256, `StoreManifest`, under the 25 GB cap
|
||
|
||
**Do NOT use `appContainer`** — see the invariant in item 6.
|
||
|
||
## 3. ☐ Remove Velopack — 3 edit sites
|
||
|
||
Only if the Store is chosen (the Store handles updates). Trivially removable; the code was
|
||
written defensively for exactly this.
|
||
|
||
- `ytLive.csproj:92` — `<PackageReference Include="Velopack" Version="1.2.0" />`
|
||
- `App.xaml.cs:3` — `using Velopack;`
|
||
- `App.xaml.cs:38-46` — the sole call site, already try/caught and commented
|
||
"(non-fatal) … when no URL is set, the check is a no-op"
|
||
|
||
**Retires:** the pending "Velopack update URL" item (`TASKS.md` open items,
|
||
`task-10-monetization.md:43`) and the self-hosted DigitalOcean droplet.
|
||
|
||
## 4. ☐ Windows App Certification Kit
|
||
|
||
Run before submission. Technical compliance is tested by WACK; it is not optional. Known
|
||
relevant check: the app must start promptly, stay responsive, and shut down gracefully
|
||
(10.4.2).
|
||
|
||
## 5. ☐ Certification notes + the three documentation artifacts
|
||
|
||
**In Partner Center (submission):**
|
||
|
||
- ☐ **Working YouTube demo account** (10.3.1) — required, we cannot stream without sign-in
|
||
- ☐ Tick the **secure third-party purchase API** box — Polar (10.8.1 / 10.8.2)
|
||
- ☐ **The 11.12 UGC position**, stated in full — "mirrored from YouTube, which moderates it
|
||
at industrial scale upstream; we render transiently, persist nothing, and provide no
|
||
user-to-user communication surface." Full reasoning and the Q1-2026 enforcement numbers
|
||
are in `research-store-certification.md` §9
|
||
- ☐ The **YouTube age-gate argument** — operator is 13+ by construction (§8)
|
||
- ☐ Note that the product is **general audience, not directed at under-13s**
|
||
|
||
**On `llamachile.shop`:**
|
||
|
||
- ☐ **Privacy policy** (10.5.1 — mandatory for Win32/Desktop Bridge). Must state: camera/mic
|
||
access is user-directed and OS-gated; **no retention** of chat or reward payloads; no
|
||
viewer data collected; **ffmpeg is bundled and nothing is fetched at runtime**
|
||
- ☐ **Code of conduct + content guidelines** (11.12 / 11.15)
|
||
- ☐ Confirm `THIRD-PARTY-NOTICES.txt` covers the bundled LGPL ffmpeg build
|
||
|
||
**In Partner Center (listing):**
|
||
|
||
- ☐ **IARC age-rating questionnaire** (10.11.1) — general audience, 12+ territory
|
||
- ☐ Title is exactly **`LlamaCasty`** — 10.1.1 forbids marketing text or extraneous keywords
|
||
- ☐ Search terms: max 7, no pricing terms, and **no other product titles** (10.1.3 — cannot
|
||
list `OBS` or `StreamYard`)
|
||
- ☐ Real listing content — 10.1.4 requires an active, substantive presence
|
||
- ☐ Review the **Individual vs Company** account question before enrolling (see
|
||
`research-store-certification.md` §11 item 1) — getting this wrong means re-enrolling
|
||
|
||
## 6. ☐ The full-trust recording invariant — **comment lands WITH this work**
|
||
|
||
`ViewModels/MainViewModel.Recording.cs:173-179` (`DefaultRecordFolder()`) writes to
|
||
`%USERPROFILE%\Downloads` or `SpecialFolder.MyVideos`; `ChooseRecordFolder()` (line 151)
|
||
uses `Microsoft.Win32.OpenFolderDialog`; the temp-write-then-move lifecycle stays in one
|
||
directory (line 131-132).
|
||
|
||
**This works only because the package is full trust.** At `mediumIL`, user-profile writes
|
||
pass through unvirtualized and `OpenFolderDialog` is an ordinary Win32 browser with no
|
||
capability token. No `broadFileSystemAccess` needed.
|
||
|
||
⚠️ **If anyone flips the manifest to `appContainer`, recording silently breaks** —
|
||
`Directory.CreateDirectory` and `File.Move` start resolving to a per-package virtualized
|
||
location and output vanishes.
|
||
|
||
**Add a comment at `DefaultRecordFolder()` in this task, not before.** A comment describing
|
||
a manifest that does not exist is worse than no comment.
|
||
|
||
**Also verify on a real packaged build** before trusting it with recordings — the docs say
|
||
full trust passes writes through, but that is worth confirming with an actual package
|
||
identity rather than an unpackaged run.
|
||
|
||
## 7. ☐ Confirm the disqualifiers stay clear after packaging
|
||
|
||
All four are currently clear (verified by grep 2026-09-27) — re-verify once the packaging
|
||
project exists:
|
||
|
||
- No Windows driver installed (we consume DShow filters, we do not install one)
|
||
- No per-user Windows service
|
||
- No elevation / `requireAdministrator` / UAC manifest
|
||
- No shell extension, no in-process module loading by outside processes, no jump list
|
||
|
||
## 8. ☐ Pre-submission gates
|
||
|
||
- ☐ 0 warnings, full suite green
|
||
- ☐ **Vertical-recording path verified end-to-end** (compositor tier is proven by
|
||
`Render_VerticalTier_Outputs_1080x1920_From_The_Center_Crop`; the *record* path has never
|
||
been exercised — `Services/Pump/FramePump.cs:645` flags off-size tiers as a known
|
||
follow-up)
|
||
- ☐ **The real-output confirmation** the creator has been doing manually
|
||
- ☐ Camera/mic/wasapi capture verified **from the packaged build**, not just unpackaged
|
||
- ☐ Clean uninstall verified (10.2.7)
|
||
- ☐ Notification-disabled path leaves the app functional (10.9)
|
||
|
||
---
|
||
|
||
## Not in this task (deliberately)
|
||
|
||
- **Velopack hardening / obfuscation / assembly splitting** — stays a GA-time decision per
|
||
TASK 36 item 6's agreed ceiling. Compile flags max at `HARDENED` + `MOCK_REWARDS`,
|
||
additive-only.
|
||
- **EULA draft/review and the THIRD-PARTY-NOTICES gate** — stay in TASK 36 item 6.
|
||
- **Vertical-canvas feature work** — the feature exists; only the *record-path test* above
|
||
is missing.
|
||
- **macOS / Avalonia port** — deferred; would be a second app, not a port.
|