diff --git a/Distribution.md b/Distribution.md index 4c42788..31b9483 100644 --- a/Distribution.md +++ b/Distribution.md @@ -1,12 +1,42 @@ # Distribution & IP Protection Plan -> 360-degree protection strategy for LlamaCasty (.NET 8 / WPF), built around Polar.sh. +> 360-degree protection strategy for LlamaCasty (.NET 8 / WPF). + +> ## ⛔ SUPERSEDED IN PART — 2026-09-27 +> +> **Distribution is now the Microsoft Store: MSIX package + Store IAP.** The creator's criteria +> were *"zero headaches, minimal maintenance (for me) while still providing accountability and +> a reasonably easy upgrade flow"*, and that ruling deleted this document's central premise. +> +> **What still stands below:** §1 (code architecture / IP protection), §5 (obfuscation posture), +> §6 (legal), §7 (hardening) — those are about protecting the *binary*, and an MSIX package is +> still a binary. **§1.2's build-posture ceiling (HARDENED + MOCK_REWARDS, additive-only) +> remains the agreed ceiling.** +> +> **What is dead:** +> - **Polar as the distribution backbone** — the entire section below. Store IAP handles +> checkout, entitlements, refunds, tax, and the customer portal. So does **file hosting**: +> the Store is the delivery mechanism, so "upload `LlamaCasty.exe` to Polar as a File +> Download benefit" (§2) no longer exists as a step. +> - **Code signing as our problem** — MSIX is signed by **Microsoft**. There is no certificate +> to buy, no HSM, no annual renewal. See §4.3, which was already corrected to say this, and +> `TASKS/research-store-certification.md` §2–3. +> - **Velopack / the update URL / the DO droplet** — Store auto-update. +> +> **The authoritative plan is now [`TASKS/task-48-distribution-msix.md`](TASKS/task-48-distribution-msix.md).** +> Keep this file for the protection sections; do not re-derive a delivery strategy from it. --- -## Polar.sh as the Distribution Backbone +## Polar.sh as the Distribution Backbone — ⛔ OBSOLETE (2026-09-27) -Polar handles everything between "customer wants to pay" and "customer has a working license key." We don't build any of that. What Polar gives us: +> **Historical record only.** Retained because the fee tables and the reasoning about *why* +> one-time beat subscription were worth working out. **None of the "How We Use It" rows are +> current**, with one exception worth keeping: the Merchant-of-Record point — *someone else +> collects and remits VAT/GST* — is still true under Store IAP, because **Microsoft** is now +> that someone. That fact alone was the strongest argument for the ruling. + +Polar handled everything between "customer wants to pay" and "customer has a working license key." We didn't build any of that. What Polar *was* going to give us: | Capability | How We Use It | |------------|---------------| @@ -18,9 +48,11 @@ Polar handles everything between "customer wants to pay" and "customer has a wor | **Merchant of Record** | Polar collects and remits VAT/GST/sales tax globally. We never touch tax compliance. | | **Webhooks** | Polar notifies our backend on purchase, cancellation, key rotation. Used for optional telemetry (section 6.3). | -**Polar pricing (2026):** 5% + $0.50 per transaction (Starter plan). No monthly fee. +**Polar pricing (2026):** 5% + $0.50 per transaction (Starter plan). No monthly fee. ⛔ *No +longer paid to anyone — the equivalent cost is now inside the Store revenue share, whose rate +is unverified (see `TASKS/research-store-certification.md` §11).* -**What Polar does NOT handle:** Obfuscation, code signing, anti-tamper, runtime protection, EULA, DMCA. That's all us — sections 1-3, 5-7 below. +**What Polar did NOT handle:** Obfuscation, code signing, anti-tamper, runtime protection, EULA, DMCA. That's all us — sections 1-3, 5-7 below. --- diff --git a/HANDOFF.md b/HANDOFF.md index 070bb5b..42829b0 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -1,75 +1,61 @@ # HANDOFF — current state **Branch:** `main` (pre-1.0, no feature branches — creator ruling 2026-08-24). -**Last pushed:** `938c5b3` (alert ticker). This unit (distribution/certification research) is -**docs-only** — no code touched, no build, no tests run. +**Last pushed:** `e78c58f` (Store certification research). This unit (recording the Store MSIX ++ Store IAP ruling) is **docs-only** — no code touched, no build, no tests run. -## This unit: distribution & Store certification research is WRITTEN DOWN +## ✅ DECIDED 2026-09-27 — distribution is the Microsoft Store: MSIX + Store IAP -The session's open question was "how do we get this in front of users without a SmartScreen -scarewall" and it had been answered **in conversation only** — which meant the next session -would re-derive it. It is now in the map, and the conversation can be dropped. +**Creator's criteria, verbatim: "zero headaches, minimal maintenance (for me) while still +providing accountability and a reasonably easy upgrade flow."** Route A is the only option +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 are in +`TASKS/research-store-certification.md` §3 — **read that before reopening the question, not +before re-deriving it.** -| File | What it is | -|---|---| -| `TASKS/research-store-certification.md` | **new** — the authoritative research. Store Policies **7.20** (effective 2026-10-22, re-check the version), MSIX full-trust vs AppContainer, cert economics, a ⛔/✅ table of which policies bind and which don't, the camera/mic gating layers, the YouTube age + COPPA analysis, the 11.12 UGC judgment call, and the filter design constraints | -| `TASKS/task-48-distribution-msix.md` | **new** — the executable checklist. Carved out of TASK 36 item 6 (code signing / installer / Velopack URL) so distribution has one owner | -| `TASKS/task-49-chat-profanity-filter.md` | **new** — the chat filter checklist. Not blocked | -| `Distribution.md` §4.3 | **corrected** — the EV recommendation was dead (§ below) | -| `ai.md` | new "Windows packaging & distribution" section (durable invariants) + a correction on the FFmpeg locator | -| `MyMistakes.md` | the policy-applicability lesson | -| `TASKS.md` | rows 48/49, a research index, and the TASK 36 item 6 split | -| `MARCOM.md` | ⛔ **privacy-copy guard — gitignored, so this edit is local-only and did not travel with the push** | +**The big consequence: the whole licensing backend is deleted.** `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` comes from the **Store +entitlement** instead of an HTTP call — which also deletes the entire "network flaky → app +thinks I'm expired" bug class. **Velopack, the update URL and the DO droplet go too.** -### Two real defects the research surfaced (both now recorded, neither fixed — no code this unit) +**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*. -1. **⛔ `FfmpegLocator` downloads an unsigned exe from GitHub and runs it.** That is Store - policy **10.2.2** (dynamic code inclusion) verbatim, *and* it is the root cause of the - 2026-09-01 failure: the previous daily pin aged out of BtbN's 14-day retention and the - download **404'd on the creator's first real recording attempt**. `FfmpegLocator.cs:30-35` - records the incident but still reads like a design choice. → **TASK 48 item 1.** Not - Store-conditional: bundling kills this whole class of cold-start failure and deletes the - startup network dependency. **This is the highest-value unblocked item in the repo.** -2. **⛔ `Distribution.md:318` recommended a $400+/yr EV certificate for a benefit Microsoft - deleted in March 2024.** Fixed. Had it shipped, it would have cost $400+/yr to buy exactly - what $150 buys. Lesson in `MyMistakes.md` — the recurring shape is *citing a policy or a - platform fact from memory instead of re-verifying it in the document itself.* +**⏳ Still open: the price.** The Store revenue share is unverified (⚠️ assume no percentage — +check current terms before setting a price), and `MONETIZATION.md`'s `$29 → $49` one-time +decision is re-opened against a fresh instinct toward a ~$99/yr subscription. **No price is +encoded anywhere until the creator settles it.** Note the storefront changed, so the old +"~$69 floor" and "don't break the launch-price promise" notes now refer to Store tiers. -### ⛔ The decision that is NOT made, and belongs to the creator +### The first code unit: bundle ffmpeg (TASK 48 item 1) -**The distribution route.** Research is done and MSIX is the front-runner (full trust is viable; -we trip **none** of the four MSIX disqualifiers — no driver installed, no per-user service, no -elevation, no shell extension). Four real combinations, costed in -`TASKS/research-store-certification.md` §3: +Two real defects the research surfaced — **neither is fixed yet**, this was a docs unit: -| # | Route | Cert/yr | SmartScreen | Notes | -|---|---|---|---|---| -| A | Store MSIX + Store IAP | **$0** | none | Polar gets deleted; revenue cut; public listing | -| B | Store MSIX + **Polar** | **$0** | none | free signing **and** keep 100% — two systems to maintain | -| C | **Polar file hosting** + own cert | $120–300 | warning ramp | **the status quo already sketched in `Distribution.md:14`/`:296`** | -| D | Store EXE (policy 10.2.9) | $120–300 | warning ramp | **dominated** — cert anyway, silent install, own URL | +1. **⛔ `FfmpegLocator` downloads an unsigned exe from GitHub and runs it.** Store policy + **10.2.2** (dynamic code inclusion) verbatim, *and* the root cause of the 2026-09-01 + failure — the pin aged out of BtbN's 14-day retention and the download **404'd on the + creator's first real recording attempt**. `FfmpegLocator.cs:30-35` records the incident but + still reads like a design choice. **Fix: bundle it.** Also deletes the startup network + dependency. +2. **⛔ `Distribution.md:318` recommended a $400+/yr EV certificate for a SmartScreen bypass + Microsoft removed in March 2024.** Fixed last commit — had it shipped, it would have cost + $400+/yr to buy what $150 buys. Now moot anyway (MSIX is signed by Microsoft), but the + lesson in `MyMistakes.md` stands: a policy citation is a claim about **scope**, not just + text. -**Nothing else was allowed to block on this** — the user is right that most of the research is -route-independent and worth banking regardless. So: research banked, dead line fixed, and only -the packaging work is parked. +### ⛔ Two invariants that break silently if touched -**Also still open, deliberately:** pricing (one-time vs one-time+monthly), the license provider, -and therefore all licensing copy. `OverlayHost.xaml` still claims Polar unlocks alerts — wrong, -alerts are not gated. Unresolved, queued, **not** to be papered over. - -### The two findings most likely to be forgotten - -- **Distribution and licensing are independent.** Policy 10.8.1 lets a **Store-distributed** - app verify licenses with **Polar**. So "should we use the Store?" does not imply "drop Polar?" - Only route A removes Polar, and that is a licensing decision riding on a distribution one. -- **⛴ Two invariants that will break silently if touched:** - - **Full trust, or recording breaks.** `DefaultRecordFolder()` writes to Downloads/MyVideos; - that passes through unvirtualized **only** at `mediumIL`. Flip the manifest to - `appContainer` and output vanishes with no error. A comment is owed there **when the - manifest lands** (TASK 48 item 6) — not before. - - **Chat is rendered, never stored.** That non-persistence half is the load-bearing part of - the 11.12 certification answer. No chat history, moderation log, analytics, or crash payload - may be added without re-arguing 11.12 first. +- **Full trust, or recording breaks.** `DefaultRecordFolder()` writes to Downloads/MyVideos; + that passes through unvirtualized **only** at `mediumIL`. Flip the manifest to + `appContainer` and output vanishes with no error. A comment is owed there **when the + manifest lands** (TASK 48 item 6) — not before. +- **Chat is rendered, never stored.** That non-persistence half is the load-bearing part of + the 11.12 certification answer. No chat history, moderation log, analytics, or crash payload + may be added without re-arguing 11.12 first. ## Landmines @@ -99,29 +85,42 @@ alerts are not gated. Unresolved, queued, **not** to be papered over. ## Uncommitted / untracked -- `MARCOM.md` — the privacy-copy guard was added but the file is **gitignored by design** - (confidential business file, `.gitignore:11`). It stays local, as intended. Same for - `MONETIZATION.md` and `CREDENTIALS.md` — **never commit those.** +- `MARCOM.md`, `MONETIZATION.md` — both edited (privacy-copy guard; the Polar-obsolete banner + and the re-opened-price note) and both **gitignored by design** (confidential business + files, `.gitignore:10-11`). They stay local, as intended. Same for `CREDENTIALS.md` — + **never commit those.** ## Next -1. **Bundle ffmpeg (TASK 48 item 1)** — unblocked, recommended on every route, and it fixes a - real user-facing failure. *This is the next code unit.* -2. **The route decision** (creator) — A/B/C above, once the ffmpeg fix is out of the way. -3. **Vertical recording verification** — the compositor tier is proven by +1. **Bundle ffmpeg (TASK 48 item 1)** — unblocked, first code unit, fixes a real user-facing + failure and clears the one hard certification gate. +2. **Settle the price** — one-time vs subscription, and the number. Then declare the IAP + products in Partner Center. **Blocks:** the Store listing, and the `OverlayHost.xaml` copy. +3. **The packaging project + `Package.appxmanifest`** (TASK 48 items 2–3) — full trust, + `webcam`/`microphone`, English only; Velopack deleted. Add the `DefaultRecordFolder()` + comment in the same unit. +4. **Polar teardown** (TASK 48 item 0) — `PolarLicenseService`, `PolarLicense`, + `MainViewModel.License.cs`, the `OverlayHost.xaml` string, the `csproj`/`App.xaml.cs` + Velopack sites. `IsPremium` ← Store entitlement. **Do this as one unit** — a half-torn-down + licensing path is worse than either end state. +5. **Verify the Store revenue-share rate** — before step 2, not after. +6. **Vertical recording verification** — the compositor tier is proven by `Render_VerticalTier_Outputs_1080x1920_From_The_Center_Crop`; the *record* path has never been run. `FramePump.cs:645` flags off-size tiers as a known follow-up. -4. **Multi-instance** — route `FfmpegLocator._toolsDir` through `InstanceProfile` (same shared - extraction race as #1, and it becomes moot if ffmpeg is bundled). Confirm the launch method: - `$env:YTLIVE_INSTANCE=2` + `dotnet run --no-build` in a second shell is the known-good path. -5. **Post-session efficacy report** — roll up `CurrentHealth` (dropped frames, duration, health +7. **WACK + certification notes + the privacy policy** (TASK 48 items 4–5) — the policy must + state camera/mic is OS-gated, nothing is retained, and nothing is fetched at runtime. +8. **Post-session efficacy report** — roll up `CurrentHealth` (dropped frames, duration, health message) when a stream or recording ends. `SessionTeardownTests` is the natural home. -6. **`scripts/publish.sh` + Debug-only `InternalsVisibleTo` + the `LlamaCasty.exe` rename** — - still queued from 2026-09-26; do **NOT** add `-p:PublishTrimmed=true` (WPF fails at runtime, - not build time). See `TASKS.md` → "Shipping / release build". -7. **The alert-gating copy** (`OverlayHost.xaml`) — fix the copy or gate the alerts; do not leave - it lying. Blocked behind the pricing ruling. -8. **The camera-privacy measurement** — does the Win11 desktop-app camera toggle actually gate a - DShow webcam and a capture card? Gates all privacy-forward copy (`MARCOM.md` guard). -9. **TASK 36:212 stale "shipped" line** — one-line fix, needs a hand. -10. Ship-checklist items live in `TASKS.md` → "1.0 gates". +9. **Measure whether the Windows camera toggle gates DShow** — gates all privacy-forward copy + (`MARCOM.md` guard). Not a certification risk; a trust risk. +10. **TASK 36:212 stale "shipped" line** — one-line fix, needs a hand. + +**Dropped from the list** because the ruling made them moot: multi-instance's ffmpeg tools-dir +race (item 1 makes it disappear), `scripts/publish.sh` + the `LlamaCasty.exe` rename (the Store +packages and delivers — *revisit only if we ever ship outside the Store*), and the alert-gating +copy (the wrong string dies with the Polar teardown in step 4). + +**Everything else in the v1 queue:** stream resilience (32), bandwidth step-down (33), scheduled +streams (34), scene-linked audio (35), the master limiter (12), text source (3.16), reward +events (3.20), the media picker (21), webcam identity (9.5), settings units A/C/D (40), and the +defaults split (37). Ship-checklist items live in `TASKS.md` -> "1.0 gates". diff --git a/TASKS.md b/TASKS.md index 0bd2384..5176ad7 100644 --- a/TASKS.md +++ b/TASKS.md @@ -29,7 +29,7 @@ | 07 | Meter scaling amplification | ✅ Done | [`TASKS/task-07-meter-scaling.md`](TASKS/task-07-meter-scaling.md) | | 08 | Audio milestone | ✅ SHIPPED 2026-08-14 | [`TASKS/task-08-audio-milestone.md`](TASKS/task-08-audio-milestone.md) | | 09 | YouTube Live Stream Management | ⏳ In progress — items 1–3, 4 shipped; item 5 open; item 6 locked (TASK 36) | [`TASKS/task-09-live-stream-management.md`](TASKS/task-09-live-stream-management.md) | -| 10 | Monetization: watermark-only one-time license + Polar billing | 🔶 In progress — steps 1–7 shipped | [`TASKS/task-10-monetization.md`](TASKS/task-10-monetization.md) | +| 10 | Monetization: watermark-only one-time license + Polar billing | 🔶 In progress — steps 1–7 shipped; **now a TEARDOWN: Polar is deleted in favour of Store IAP (2026-09-27)** | [`TASKS/task-10-monetization.md`](TASKS/task-10-monetization.md) | | 11 | Post-pause polish batch (creator's 8 review issues) | ✅ SHIPPED 2026-08-15 | [`TASKS/task-11-polish-batch.md`](TASKS/task-11-polish-batch.md) | | 12 | Master limiter on the live mix | ☐ Queued | [`TASKS/task-12-master-limiter.md`](TASKS/task-12-master-limiter.md) | | 13 | Social media launch kit | 🔶 Scoped — queued after v1 | [`TASKS/task-13-social-launch-kit.md`](TASKS/task-13-social-launch-kit.md) | @@ -63,7 +63,7 @@ | 45 | TEST-tab chat fix #2: insert body must declare `snippet.type` (400 MISSING_REQUIRED_FIELD) | ✅ Done (2026-09-25) | [`TASKS/task-45-chat-insert-type.md`](TASKS/task-45-chat-insert-type.md) | | 46 | Drawers: click outside the rail closes whichever is open — TEST added to the existing Stream Settings + YPP dismiss behavior | ✅ Done (2026-09-25) | [`TASKS/task-46-drawer-click-outside-close.md`](TASKS/task-46-drawer-click-outside-close.md) | | 47 | Alert box video: built-in/custom alert clip (+ six-animation fallback) with read-time fade in/out + ticker (now in the preview too, 3 display methods) + alert volume in the live mix | ✅ Done (2026-09-26) | [`TASKS/task-47-alert-videos.md`](TASKS/task-47-alert-videos.md) | -| 48 | Distribution & packaging: route decision, bundled ffmpeg, MSIX + code signing | ⏳ Queued — **blocked on the creator's route decision**; bundled-ffmpeg half is unblocked and recommended | [`TASKS/task-48-distribution-msix.md`](TASKS/task-48-distribution-msix.md) | +| 48 | Distribution & packaging: **MSIX + Store IAP** | 🔶 In progress — **✅ route decided 2026-09-27 (Store MSIX + Store IAP)**; item 1 (bundle ffmpeg) is the next code unit | [`TASKS/task-48-distribution-msix.md`](TASKS/task-48-distribution-msix.md) | | 49 | Chat profanity filter (local, opt-in, non-persistent, user word list) | ☐ Queued (2026-09-27) | [`TASKS/task-49-chat-profanity-filter.md`](TASKS/task-49-chat-profanity-filter.md) | --- @@ -213,18 +213,44 @@ second encoder path needing a "redirect". Record+simulcast is one ffmpeg with tw installer, and the Velopack update URL have one owner instead of three scattered mentions. **EULA draft/review and the THIRD-PARTY-NOTICES license-texts gate stay in TASK 36 item 6**, as does the agreed build-posture ceiling (HARDENED + MOCK_REWARDS, additive-only). -- **TASK 48 — distribution & packaging** — **⏳ the route decision belongs to the creator.** - Research is done and MSIX is the front-runner; the four real combinations (Store+Store IAP / - Store+Polar / Polar-hosted+own cert / dominated Store-EXE) and their cert costs are tabulated - in `TASKS/research-store-certification.md` §3. Two things are settled and unblocked: - **(a) bundle ffmpeg instead of downloading it** — `FfmpegLocator` currently downloads an - unsigned exe from GitHub and runs it, which is Store policy 10.2.2 (dynamic code inclusion) - *and* is the root cause of the 2026-09-01 expired-pin 404; **(b) the `Distribution.md` EV - claim is dead** — Microsoft removed the SmartScreen EV bypass in March 2024, so paying - $400+ buys what $150 buys. Full-trust MSIX is viable (mediumIL, not AppContainer, and the - three technical disqualifiers — drivers, per-user services, elevation — are all clear). - Packaging, Velopack removal, WACK, the demo account, the privacy policy and the IARC - questionnaire are all specified in the task file and waiting. +- **TASK 48 — distribution & packaging** — **✅ ROUTE DECIDED 2026-09-27: Microsoft Store, + MSIX package, Store IAP.** Creator's criteria, verbatim: *"zero headaches, minimal + maintenance (for me) while still providing accountability and a reasonably easy upgrade + flow."* Route A is the only option 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 + (Store+Polar, Polar-hosted + own cert, Store-EXE) are recorded with their reasons in + `TASKS/research-store-certification.md` §3 so the decision is not reopened. + - **The consequence that makes it cheap: the entire licensing subsystem is deleted.** + `Services/PolarLicenseService.cs` (HTTP validation, swallowed network failures, the + ignored `expires_at`), `Helpers/PolarLicense.cs`, `ViewModels/MainViewModel.License.cs` + (`PremiumUrl`, customer portal, the `OfflineGracePeriod = 14 days` subscription-era + artifact, renewal/lapse copy), and the incorrect "Polar unlocks alerts" string in + `Controls/OverlayHost.xaml` all become dead code. `IsPremium` is derived from the **Store + entitlement** instead of a remote HTTP call — one locally cached bit refreshed by the OS, + and the whole "network flaky → app thinks I'm expired" bug class disappears with the + offline-grace machine. + - **Unchanged:** the watermarks posture. 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):** current Store revenue-share terms — ⚠️ do + not assume any percentage, verify before setting a price; and the one-time vs + subscription shape of the IAP tier (the storefront is decided, the price is not). + - **First code unit: item 1 — bundle ffmpeg instead of downloading it.** Unblocked, + recommended on every route, and it fixes a real user-facing failure. +- **TASK 10 is now a teardown, not a build** (2026-09-27) — Store IAP deletes the Polar + licensing path. The Polar fee tables, the perpetual-key model, and the customer-portal + plumbing in `MONETIZATION.md` (gitignored, local) are obsolete; the **watermark-only + entitlement and the branding-flash delta carry over unchanged**. The stale Polar product + (`$99/yr`, id `d105dfa1-…`) is not reused — Store IAP products are declared in Partner + Center instead. +- **Pricing is re-opened** (2026-09-27) — `MONETIZATION.md` carries a **one-time perpetual** + decision from 2026-09-21 (`$29` founder → `$49` list) that explicitly superseded the old + `$99/yr` subscription; the creator's current instinct is back toward a subscription. + **Unresolved — do not encode a price until it is settled**, and note that Store IAP moves + pricing into fixed Store tiers, so the "floor" (`~$69` per `MONETIZATION.md`) and the + "don't break the launch-price promise" note now refer to a different storefront. - **TASK 49 — chat profanity filter** — queued, not blocked, size S. Local, on-device, **non-persistent**, opt-in, **user-supplied word list (never a hardcoded slur list)**, and it must **never match the SuperChat amount or reward fields** (financial data, Store 10.5.5). diff --git a/TASKS/research-store-certification.md b/TASKS/research-store-certification.md index e27f3c1..51cd391 100644 --- a/TASKS/research-store-certification.md +++ b/TASKS/research-store-certification.md @@ -14,13 +14,18 @@ ## 1. Bottom line +**✅ ROUTE DECIDED 2026-09-27 — the creator chose route A: Microsoft Store, MSIX package, +Store IAP.** The research below is what the decision was made from, and it stays as the +audit trail so the choice is never re-litigated. The executable checklist is +`TASKS/task-48-distribution-msix.md`; the ruling and the criteria behind it are in that +file's item 0. + **MSIX packaging is viable.** One real blocker, and it is a code change we should want anyway (§4). Three technical blockers that normally kill a Store submission — drivers, per-user services, elevation — we **do not trip** (§7). -**The decision is NOT made.** The route is open; this file records the options and their -costs so the decision can be made from a clear page. `Distribution.md` holds the older -plan and contains **one factually dead claim** — see §2. +**Nothing else is open on distribution.** The only number still unverified is the Store +revenue-share rate (§11 item 4), which is a *pricing* input, not a distribution one. --- @@ -74,20 +79,28 @@ Also worth knowing before budgeting: | **C** | Polar file hosting | Polar | $150–300/yr | no | | **D** | Store EXE (10.2.9) | Polar | $150–300/yr | no | +**✅ A was chosen (2026-09-27)** — the creator's criteria were "zero headaches, minimal +maintenance (for me) while still providing accountability and a reasonably easy upgrade +flow," and A is the only option where *every* one of those is solved by handing the work to +Microsoft rather than to a certificate vendor. The rest of this section stays as the record +of what was rejected and why, so the decision is not reopened by a later session that +remembers only that "certs cost $150/yr." + **A** is the only one where Polar becomes unnecessary — the Store handles payment, -entitlement, and refunds, and you would delete `PolarLicenseService` and the -customer-portal plumbing. That is a *distribution* choice that happens to subsume -licensing. +entitlement, and refunds, and `PolarLicenseService` and the customer-portal plumbing are +deleted. That is a *distribution* choice that happens to subsume licensing. -**B** is the compromise worth serious consideration: the Store solves the SmartScreen -problem and the annual cert bill while Polar stays the licensing system and we keep 100% -of revenue. Cost is two systems to maintain. +**B** is the compromise that was **not** taken: the Store would have solved the SmartScreen +problem and the annual cert bill while Polar stayed the licensing system. Rejected because +it keeps a licensing backend, a customer portal, activation limits, and an offline-grace +machine alive — the exact maintenance the ruling was made to eliminate. -**C** is the status quo already sketched in `Distribution.md:14` / `:296` — Polar hosts the -signed exe (10GB, signed personal download URLs, SHA-256 included). **The Store is not -needed for delivery at all.** The Store's only unique value is *free Microsoft signing with -no SmartScreen warning*, and its cost is MSIX packaging work, Store review, and a public -listing. +**C** is the status quo previously sketched in `Distribution.md:14` / `:296` — Polar hosts +the signed exe (10GB, signed personal download URLs, SHA-256 included). **The Store is not +needed for delivery at all**; its only unique value is *free Microsoft signing with no +SmartScreen warning*, and its cost is MSIX packaging work, Store review, and a public +listing. Note the irony: C is the only option with a recurring cost *and* the only one where +the creator maintains payment plumbing themselves. **D is dominated** — strictly worse than both A and C: cert required anyway, silent install required, and we maintain the versioned download URL. Documented in one line so it is diff --git a/TASKS/task-48-distribution-msix.md b/TASKS/task-48-distribution-msix.md index eb0c148..dd9337c 100644 --- a/TASKS/task-48-distribution-msix.md +++ b/TASKS/task-48-distribution-msix.md @@ -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` — `` @@ -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** diff --git a/ai.md b/ai.md index 9021dea..ddada1a 100644 --- a/ai.md +++ b/ai.md @@ -1794,14 +1794,34 @@ published decisions (recorded in `TASKS/task-43-native-alerts.md`): --- -## Windows packaging & distribution (2026-09-27 — research settled, route NOT decided) +## Windows packaging & distribution (✅ DECIDED 2026-09-27 — Store MSIX + Store IAP) -Full analysis: `TASKS/research-store-certification.md` (Store Policies **7.20**, effective -2026-10-22 — re-check the version before acting). Executable checklist: -`TASKS/task-48-distribution-msix.md`. **The route is the creator's decision** (Store MSIX + -Store IAP / Store MSIX + Polar / Polar-hosted + own cert / dominated Store-EXE). Do not build -packaging work before that call, and do not re-derive the options — they are tabulated in the -research file §3. +**The distribution route is settled: Microsoft Store, MSIX package, Store IAP.** Creator's +criteria, verbatim: *"zero headaches, minimal maintenance (for me) while still providing +accountability and a reasonably easy upgrade flow."* Route A was chosen because it is the +only option 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 live in +`TASKS/research-store-certification.md` §3 — **read that before reopening the question**, not +before re-deriving it. Executable checklist: `TASKS/task-48-distribution-msix.md`. + +**Consequences that are architecture, not packaging detail:** + +- **⛔ Velopack and the update URL are deleted** — the Store auto-updates. 3 edit sites, + `ytLive.csproj:92` + `App.xaml.cs:3,38-46`. The self-hosted DO droplet dies with it. +- **⛔ The Polar licensing subsystem is deleted** — `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 in `OverlayHost.xaml`. **`IsPremium` is derived from the + Store entitlement**, not a remote HTTP call. That deletes the entire "network flaky → app + thinks I'm expired" bug class along with the offline-grace machine. +- **⛔ Bundle ffmpeg — no runtime downloads** (Store policy 10.2.2; also the 2026-09-01 404). + See the FFmpeg locator section above. **This is the first code unit.** + +**What does NOT change: the monetization posture.** Free gets everything; the branding flash +stays the **only** paid delta (`TASK 36` item 2). Store IAP changes how `IsPremium` is +*obtained*, never what it *gates*. `Monetization` above still governs. ### ⛔ Durable invariant: full trust, or recording silently breaks @@ -1833,15 +1853,15 @@ line item) and private keys must live on an **HSM or hardware token**. The **Sto needs no certificate at all** — Microsoft re-signs. `Distribution.md` §4.3 was corrected 2026-09-27; it previously recommended EV. -### Distribution and licensing are independent +### Distribution and licensing are independent — but the ruling collapsed them -A **Store-distributed** app may still verify licenses with **Polar** (policy 10.8.1 -explicitly permits a secure third-party purchase API for non-game PC products; the box is -ticked in Partner Center). So "should we use the Store?" does **not** imply "should we drop -Polar?" — the only combination that removes Polar is Store + Store IAP, and that is a -*licensing* choice which happens to ride on a *distribution* decision. (The monetisation -posture itself is unchanged and lives in the Monetization section above: free gets -everything; the branding flash is the only paid delta.) +A **Store-distributed** app *may* still verify licenses with **Polar** (policy 10.8.1 +explicitly permits a secure third-party purchase API for non-game PC products), so "should we +use the Store?" did not logically imply "drop Polar?" — route B was a real option. **The +creator chose route A instead**, so Polar is gone. Recording the distinction because it was +the reason the decision was thought-through rather than assumed: the licensing choice rode on +a distribution decision, and the *only* route that removed the licensing backend was the one +that also removed the certificate. Both burdens had the same single solution. ### ⛔ "Only when the user says so" is enforced by Windows, not by us