docs: distribution route decided — Microsoft Store MSIX + Store IAP

Creator ruling 2026-09-27. Criteria, verbatim: "zero headaches, minimal
maintenance (for me) while still providing accountability and a reasonably
easy upgrade flow." Route A is the only combination where all four are solved
by handing the work to Microsoft rather than to a certificate vendor: $0/yr,
no certificate, no HSM, no annual renewal, no SmartScreen ramp — plus Store
auto-update, Store-side payments/entitlements/refunds/support, and Microsoft
review as the accountability layer.

The rejected options and their reasons stay in research-store-certification.md
§3 so a later session reads the ruling instead of re-deriving it.

What this deletes:
- The entire licensing backend. PolarLicenseService, PolarLicense,
  MainViewModel.License.cs (PremiumUrl, customer portal, the OfflineGracePeriod
  = 14 days subscription-era artifact, renewal/lapse copy) and the wrong
  "Polar unlocks alerts" string all become dead code. IsPremium is derived from
  the Store entitlement instead of an HTTP call, which also removes the whole
  "network flaky -> app thinks I'm expired" bug class.
- Velopack, the update URL, and the self-hosted droplet — the Store updates.
- Distribution.md's premise: Polar as the distribution backbone, Polar file
  hosting, and code signing as our problem. The IP-protection sections (1, 5,
  6, 7) still stand and the build-posture ceiling is unchanged.

What does NOT change: the entitlement. Free gets everything; the branding
flash stays the only paid delta. Store IAP changes how IsPremium is obtained,
never what it gates.

Still open, deliberately: the price. The Store revenue share is unverified (do
not assume a percentage), and MONETIZATION.md's $29 -> $49 one-time decision is
re-opened against a fresh instinct toward ~$99/yr. No price encoded yet.

The first code unit is unchanged: bundle ffmpeg (TASK 48 item 1). That clears
the one hard certification gate and fixes a real user-facing 404.

MARCOM.md and MONETIZATION.md were edited too but are gitignored by design, so
those changes stayed local.
This commit is contained in:
2026-09-27 14:23:51 -07:00
parent e78c58fc28
commit 85fad1ab79
6 changed files with 282 additions and 162 deletions
+65 -35
View File
@@ -5,40 +5,73 @@
> 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)
**Status:** 🔶 In progress — **✅ ROUTE DECIDED 2026-09-27: Microsoft Store MSIX + Store IAP.**
Polar is deleted; every licensing subsystem goes with it.
**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
## 0. ✅ THE DECISION — Microsoft Store, MSIX, Store IAP (creator ruling 2026-09-27)
Pick one:
**Creator's stated criteria, verbatim: "zero headaches, minimal maintenance (for me) while
still providing accountability and a reasonably easy upgrade flow."**
| # | 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 |
**Ruling: distribute as an MSIX package through the Microsoft Store, and take payment through
Store IAP.** All four criteria are satisfied by the same mechanism:
**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*.
| Criterion | How MSIX + Store IAP satisfies it |
|---|---|
| Zero headaches | **$0/yr** — Microsoft holds the signing certificate, the reputation, and the installer. No cert, no HSM, no annual renewal, no SmartScreen ramp |
| Minimal maintenance | Microsoft handles **updates**, **payments**, **entitlements**, **refunds**, and **customer support**. Nothing to run |
| Accountability | Microsoft reviews every submission — that *is* the accountability layer, and it is a real one |
| Easy upgrade flow | Store auto-update. **Velopack, the update URL, and the DO droplet all go** |
Cost table, full detail, and the dead-EV correction: `research-store-certification.md` §2–3.
### The consequence that makes this cheap: the entire licensing subsystem is deleted
Choosing Store IAP over Store+Polar is what makes "minimal maintenance" real. Keeping Polar
would have left the creator maintaining a licensing backend, a customer portal, activation
limits, and an offline-grace machine — the exact burden the ruling was made to avoid.
**Dead code / deleted concepts (TASK 10 is now a teardown, not a build):**
- `Services/PolarLicenseService.cs` — HTTP validation, the swallowed network failures, the
ignored `expires_at`
- `Helpers/PolarLicense.cs` — the record type
- `ViewModels/MainViewModel.License.cs` — `PremiumUrl`, the customer portal, the
`OfflineGracePeriod = 14 days` subscription-era artifact, renewal/lapse copy
- `Controls/OverlayHost.xaml` — the incorrect "Polar unlocks alerts" copy resolves itself
- `ytLive.csproj:92` Velopack `PackageReference` + `App.xaml.cs:3,38-46` — see item 3
- The `MONETIZATION.md` provider sections (gitignored — local file)
**What replaces it:** `IsPremium` is derived from the **Store entitlement** instead of a
remote HTTP call. One bit, locally cached, refreshed by the OS. The offline-grace machine
disappears because the OS supplies license state — and with it the whole class of
"network flaky → app thinks I'm expired" bugs.
⚠️ **The watermarks posture is unchanged:** free gets everything; the branding flash stays the
ONLY paid delta (TASK 36 item 2). Store IAP changes how the bit is *obtained*, never what it
*gates*.
### Still to verify (does not block packaging work)
- ☐ **Current Store revenue-share terms.** ⚠️ **Do not assume any percentage** — the terms have
moved more than once and smaller indie apps sometimes qualify for better rates. **Verify
before setting a price.** Microsoft also requires that any IAP products and their pricing be
declared in Partner Center.
- ☐ **Store IAP tier shape** — one-time vs subscription. The creator's one-time-vs-both
question is still open; the storefront it is sold through is now decided.
- ☐ Individual vs Company Partner Center account (10.8.3 / 10.14 — see
`research-store-certification.md` §11 item 1). **Get this right before enrolling**; a
Store+IAP product needing financial info for primary functionality would require Company,
but a free product with a paid upgrade tier is the normal consumer shape and is fine on an
individual account.
---
## 1. ⛔ Bundle ffmpeg instead of downloading it — **do this on EVERY route**
**Not conditional on the Store. This is worth doing regardless.**
**This is item 1 because it is genuinely first** — it fixes a user-facing failure on its own and is a hard certification gate.
`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
@@ -66,14 +99,12 @@ 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.
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
- `uap10:TrustLevel="mediumIL"` and `uap10:RuntimeBehavior="packagedClassicApp"` — the
latter 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
@@ -82,7 +113,7 @@ so this is purely additive.
## 3. ☐ Remove Velopack — 3 edit sites
Only if the Store is chosen (the Store handles updates). Trivially removable; the code was
The Store handles updates, so this is simply **deleted**. Trivially removable; the code was
written defensively for exactly this.
- `ytLive.csproj:92` — `<PackageReference Include="Velopack" Version="1.2.0" />`
@@ -103,14 +134,15 @@ relevant check: the app must start promptly, stay responsive, and shut down grac
**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)
- ☐ **The IARC age-rating questionnaire** (10.11.1) — general audience, 12+ territory
- ☐ **Declare the IAP products and their pricing** in Partner Center (required for Store IAP)
- ☐ Note that the product is **general audience, not directed at under-13s**
- ☐ The 11.12 UGC position is **less load-bearing now that Polar is gone** — the strong part of
the original argument was "we render transiently, persist nothing, and provide no
user-to-user communication surface," which is *unaffected*. State it in full anyway; the
reasoning and the Q1-2026 YouTube enforcement numbers are in
`research-store-certification.md` §9
- ☐ The **YouTube age-gate argument** — the operator is 13+ by construction (§8)
**On `llamachile.shop`:**
@@ -122,13 +154,11 @@ relevant check: the app must start promptly, stay responsive, and shut down grac
**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
- ☐ Review the **Individual vs Company** account question before enrolling
## 6. ☐ The full-trust recording invariant — **comment lands WITH this work**