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.
27 KiB
ytLlive — Windows Store & code-signing certification research (authoritative)
Catalog:
TASKS.md· memory map:schema.md· distribution plan:Distribution.mdResearched 2026-09-27 against Microsoft Store Policies version 7.20 (published 2026-09-15, effective 2026-10-22) and the MSIX packaging docs. Policy version moves — re-check the version before acting on any line here.
This file is the "why". The executable checklist lives in
task-48-distribution-msix.md. No code changed as a result of this research; it only decides what task-48 will eventually do.
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).
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.
2. ⚠️ The one line in Distribution.md that is now wrong
Distribution.md:318 currently says:
Buy an EV (Extended Validation) code-signing certificate from DigiCert or Sectigo ($200-400/year). EV certificates get instant SmartScreen reputation; DV/OV certs trigger SmartScreen warnings for months.
The EV half of that is false. Microsoft removed the SmartScreen instant-bypass for EV certificates in March 2024. An EV-signed file now accumulates reputation exactly the way an OV-signed file does. Paying $400+/yr buys precisely what paying $150 buys.
schema.md rule 4: stale facts get FIXED, not appended. Corrected in Distribution.md
(2026-09-27).
Also worth knowing before budgeting:
- Cert validity is capped at 460 days (CA/B Forum CSC-31, effective 2026-03-01). This is a recurring annual line item, not a one-time purchase.
- Private keys must live on an HSM or hardware token (CA/B rule since 2023-05). Budget for the token or a cloud HSM.
- Signing does not clear the warning on day one. A valid cert stops the malware warning immediately; the "unrecognized publisher" warning clears as SmartScreen accrues reputation from real download volume. Expect a ramp on every route.
- Third-party AV vendors layer their own reputation blocking on top of SmartScreen, so signed-but-new can still warn on consumer machines. The Store route is the only one that is warning-free, because Microsoft holds the reputation.
3. Signing economics
| Route | Cert cost | SmartScreen | Install UX | Notes |
|---|---|---|---|---|
| Microsoft Store, MSIX package | $0 — Microsoft re-signs | No warning | Normal | Store review, public listing, updates handled by Store |
| Microsoft Store, EXE/MSI (10.2.9) | $150–300/yr — your own cert | Warning ramp | Silent install only | Store does NOT re-sign; you maintain a versioned URL |
| Direct + OV cert | $150–300/yr | Warning ramp | Yours | Worldwide availability |
| Direct + Azure Artifact Signing | ~$9.99/mo ≈ $120/yr | Warning ramp | Yours | Individuals: USA/Canada only. Identity validation required |
| Direct + EV cert | $400+/yr | Same as OV | Yours | Do not buy. No benefit since March 2024 |
| Unsigned direct | $0 | Strong block | Yours | Unusable for public download |
Four real combinations
| # | Distribute | Verify payment | Cert | Revenue cut |
|---|---|---|---|---|
| A | Store | Store IAP | $0 | yes |
| B | Store | Polar | $0 | no |
| 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 PolarLicenseService and the customer-portal plumbing are
deleted. That is a distribution choice that happens to subsume licensing.
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 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 never re-litigated.
4. ⛔ The one real blocker: 10.2.2 (dynamic code inclusion)
Policy 10.2.2:
Your product must not attempt to fundamentally change or extend its described functionality or introduce features or functionality that is in violation of Store Policies through any form of dynamic inclusion of code. Your product should not, for example, download a remote script and subsequently execute that script in a manner that is not consistent with the described functionality.
What we do today: Services/Encoder/FfmpegLocator.cs:37-38 pins a GitHub release URL,
and LocateAsync downloads the zip (line 69), extracts it (line 73), and the encoder then
executes that binary. On a cold cache the app downloads an unsigned executable from the
internet and runs it.
That is the pattern 10.2.2 names. The word is "script," so a narrow reviewer might quibble about a PE binary — but 10.2.3 reinforces it, and 10.2.9 separately requires all PE files to chain to the Microsoft Trusted Root Program, which a downloaded BtbN build does not.
The fix: bundle ffmpeg.exe + ffprobe.exe + the libav*.dll family inside the
package instead of downloading. No dynamic code inclusion, so 10.2.2 is satisfied.
This is a fix we want regardless of route. 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 that entire class of cold-start failure permanently,
and it also removes the startup network dependency.
The LGPL-shared licensing reasoning in FfmpegLocator is unaffected — dynamic linking was
the deliberate choice for LGPL §6 compliance, not for download avoidance. Confirm
THIRD-PARTY-NOTICES.txt (already shipped) covers the bundled build.
What I got wrong initially: I believed MSIX might forbid launching a bundled executable. It does not — see §5.
5. MSIX technical facts (full trust vs AppContainer)
MSIX packages run at one of two trust levels, chosen by the manifest:
| Trust level | Behavior |
|---|---|
mediumIL / full trust |
Same permissions as a standard desktop app. Package identity + some file/registry virtualization. Can spawn child processes. ← this is what a WPF broadcast tool wants |
appContainer |
Strictly isolated. Process and children see only explicitly granted resources. File/registry virtualized. |
A packaged WPF app declaring runFullTrust runs at medium integrity — it is not an
AppContainer. Bundling and launching ffmpeg from inside the package is therefore fine.
This is the "Desktop Bridge" / packagedClassicApp model.
Critical file-write consequence: "Writes to user profile locations (such as AppData) are redirected to per-package locations for AppContainer apps, or pass through for full-trust apps." The package install folder itself is read-only.
Manifest this app needs:
rescap:Capability Name="runFullTrust"— medium integritywebcamandmicrophonecapabilitiesuap10:TrustLevel="mediumIL"uap10:RuntimeBehavior="packagedClassicApp"
The full-trust recording invariant (durable architectural fact)
ViewModels/MainViewModel.Recording.cs writes recordings to
%USERPROFILE%\Downloads or SpecialFolder.MyVideos (DefaultRecordFolder(), line
173–179), with Microsoft.Win32.OpenFolderDialog for overrides (ChooseRecordFolder(),
line 151). The whole temp-write-then-move lifecycle stays inside one directory
(line 131–132).
This works only because we are full trust. At mediumIL those writes pass through
unvirtualized and OpenFolderDialog is an ordinary Win32 folder browser with no capability
token involved. No broadFileSystemAccess is needed.
⚠️ If anyone ever flips the manifest to appContainer, recording silently breaks —
Directory.CreateDirectory and File.Move start resolving to a per-package virtualized
location. A code comment belongs at DefaultRecordFolder() when the manifest lands, not
before (a comment describing a manifest that does not exist is worse than no comment).
6. Store policy verdicts — what binds, what doesn't
Policy 7.20. "Binds" = must be satisfied to submit.
⛔ Binds — blockers or real work
| Policy | Requirement | Our status |
|---|---|---|
| 10.2.2 | No dynamic code inclusion (download-and-execute) | ❌ VIOLATED — see §4. Fix: bundle ffmpeg |
| 10.3.1 | Login required → supply a working demo account in certification notes | ☐ TODO — we require YouTube sign-in |
| 10.5.1 | Privacy policy mandatory; URL in Partner Center. Desktop Bridge + Win32 always need one. Must state what PI is accessed/collected/transmitted, how used/stored/secured, types of parties disclosed to, controls the user has, compliance with law, and be kept current | ☐ TODO — llamachile.shop can host it |
| 10.5.4 | Collect/store/transmit PI securely with modern cryptography | ✅ DPAPI for tokens, HTTPS to YouTube |
| 10.5.5 | No highly sensitive PI (health/financial) unless tied to functionality + express consent | ⚠️ SuperChat/reward path touches financial data — keep display-text-only processing, never log payloads |
| 10.6 | Declared capabilities must relate to the product's functions; must not circumvent OS checks | ✅ camera/mic for a broadcast tool is self-evident; must use standard MF/DShow/WASAPI paths |
| 10.7 | Localize for every declared language; declare English only | ☐ Keep the manifest to one language |
| 10.1.1 | Title must be unique, no marketing or descriptive text, no extraneous keywords | Store name is exactly LlamaCasty |
| 10.1.3 | Search terms: max 7, no pricing terms, no other product titles unless you published them | ☐ Cannot list OBS or StreamYard as search terms |
| 10.1.4 | Distinct informative metadata + active Store presence | ☐ Listing must not be a stub |
| 10.2.7 | Must enable clean uninstall | ☐ MSIX gives this free |
| 10.4.2 | Start promptly, stay responsive, shut down gracefully, survive native API exceptions | ✅ AppLog + dispatcher handlers exist |
| 11.11.1 | IARC age-rating questionnaire required | ☐ General-audience streaming tool, 12+ territory |
| 11.11.2 | Keep the rating accurate as features change | ☐ Re-visit if audience-facing features shift |
| 11.12 | User Generated Content — see §9 | ⚠️ Judgment call, documented not built |
| 10.8.1 | Non-game PC products may use a secure third-party purchase API or Store IAP | ✅ Polar explicitly permitted. Must tick the third-party purchase box in Partner Center |
| 10.8.2 | Third-party purchase: identify provider, authenticate user, obtain confirmation, PCI DSS, note in Partner Center | ☐ Covered by Polar; tick the box |
| 10.8.3 | Individual accounts cannot require financial info for primary functionality; requiring it forces a Company account | ⚠️ Open question — see §11 |
| 10.14 | Individual accounts usually fine for a solo dev; Company required if the product needs financial info for primary functionality, or a consumer would read the name as a business | ⚠️ Same question as 10.8.3 |
| 10.8.7 | Pricing must follow the FTC Guides Against Deceptive Pricing and must not be irrationally high vs. features | ☐ A pricing decision, not a code task |
| 10.5.8 | Must follow applicable safety/privacy laws re children | ☐ Follow-the-law obligation — see §8 |
| 10.1.5 | May enable acquisition of your other Store products or add-ons | N/A |
✅ Does NOT bind — recorded so it is never re-raised
| Policy | Why it doesn't apply |
|---|---|
| 10.10 (Advertising) | Ads are injected server-side by YouTube after the RTMP handoff. LlamaCasty displays no ads, renders no ad SDK, and controls no ad decision. Every 10.10 clause is conditioned on ads your product displays. Not applicable. |
| 10.2.5 (Store-only install) | 7.20 applies "installed and updated only through the Store" to game products and Xbox console products. A non-game PC app is not locked to MSIX. (The 2018 v7.7 wording said "all of your apps" — that language has been narrowed.) |
| 10.2.3 (secondary software) | ffmpeg enhances the product's functionality, and once bundled it is not "offered to install" as separate software |
| 10.2.8 (Windows settings) | RegisterHotKey and standard DShow/MF paths are supported APIs. No accessibility-API or undocumented-API tricks |
| 10.10.1 / 10.10.2 | Follows from 10.10 — no ads displayed |
| 11.13 (Storefront) | Not a storefront |
| 11.14 (Gambling) | Not applicable |
| 11.15 (Child Safety) | Prohibits products enabling exploitation/grooming/sexualization/sextortion/trafficking, and adult-themed products targeting children. A broadcast tool is not in this class. Nothing LlamaCasty does creates, uploads, or distributes child-exploiting content |
| 10.5.2 | We do not publish a customer's PI to an outside service beyond the stream they themselves broadcast |
| 10.5.7 (location) | No device location access |
| 10.2.9 | Only if we chose the Store EXE route. It would bind — see §3, route D is dominated |
Certification mechanics
- Technical compliance is tested by the Windows App Certification Kit before submission.
- Certification takes up to 3 business days; the listing appears ~15 min after publish.
- MSIX package requirements: SHA2-256 block map,
StoreManifest, 25 GB max. - Partner Center account: individual vs company — see §11.
7. MSIX disqualifiers we do NOT trip
Verified by grep across the codebase 2026-09-27. The MSIX prep docs list four hard disqualifiers; all four are clear:
| Disqualifier | Our status |
|---|---|
| "Your application requires a Windows driver. MSIX doesn't support Windows drivers." | ✅ We consume DShow filters, we don't install a driver. (A creator's third-party Virtual Capture Device is their hardware, not something we ship.) |
| "Your application requires a user Windows service. MSIX doesn't support per-user Windows services." | ✅ No service installation anywhere |
| Apps requiring elevation for any functionality are not accepted | ✅ No requireAdministrator / highestAvailable / UAC manifest |
| "Your app's modules are loaded in-process to processes that are not in your Windows app package" (shell extensions, etc.) | ✅ No shell extensions, no in-process module loading, no jump lists |
Also verified: there is no .manifest file in the repo at all today, so packaging is
purely additive — a new Package.appxmanifest plus a packaging project. The existing
unpackaged exe keeps working.
Other store rules worth knowing while listing
- 10.1.4 requires an active Store presence — the listing cannot be a placeholder.
- 10.7 — declare English only, or localize the description into every declared language.
- 10.9 — the app must stay functional when notifications are disabled. Our toast stack must not be load-bearing.
8. Minors, COPPA, and the age question
The operator is 13+ by construction
YouTube Terms of Service:
- "You must be at least 13 years old to use the Service"
- 13–17 permitted with parent/guardian permission
- Under 13 cannot create an account — exceptions are YouTube Kids and supervised accounts via Family Link or Google Workspace for Education
- Google Accounts: 13 is the minimum to self-manage
- The floor varies by region — at least one YouTube ToS variant states 14, tracking EU digital-consent ages. Treat it as 13–14, not a fixed 13.
Why this does real work: LlamaCasty requires a YouTube account to operate, because streaming requires one. So the person running the broadcast is 13+ by construction. A COPPA theory premised on "a service collecting personal information from children under 13" has no purchase against the operator — and that is true by construction, not by promise. That is the cleanest sentence available for a certification note.
The residual exposure
The viewers may still be under 13 on supervised/family-linked accounts, so the chat ingestion point does not fully disappear. It weakens substantially, because:
- Live chat is restricted/disabled on supervised YouTube kid accounts
- YouTube moderates the messages before we ever see them (§9)
- Our mitigation: retain nothing — parse, display, discard (see
task-49)
COPPA is very unlikely to bind
COPPA applies to child-directed services and to operators collecting PI from children under 13. The three-factor test (subject matter, audience, evidence of child focus) is nowhere near met by a broadcast tool aimed at a gaming audience. 10.5.8 is a follow-the-law obligation, not a be-child-safe obligation — it says "follow all applicable laws," it does not impose COPPA duties on us directly.
But note the enforcement trend (FTC, September 2025 Apitor settlement): operators are held responsible for what third-party components collect on their behalf, and the FTC expects a real inquiry into third-party collection practices. That case was about an SDK; the principle is what matters for a chat pipe. This is a documentation obligation, and the answer is "we integrate no third-party analytics or ad SDKs, and we persist no chat or reward payloads."
9. 11.12 User Generated Content — the judgment call
11.12 triggers on "content that users contribute to a product and which can be viewed or accessed by other users in an online state." Chat is literally that. If a reviewer classifies the chat surface as UGC, the requirements are:
- Published terms of service, code of conduct, and/or content guidelines for UGC, accessible in-product and on your website
- A means for users to report inappropriate/illegal/harmful UGC to the developer for review, and/or proactive detection
- Respect user safety, parental, and content privilege settings, and handle restricted access gracefully
- Remove or disable UGC when requested by Microsoft
- Disclose that content is not created by the developer/publisher where appropriate
The argument: YouTube discharges the moderation obligation upstream
| 11.12 asks for | How YouTube already covers it |
|---|---|
| Published ToS / CoC / content guidelines | Community Guidelines, linkable from our listing |
| A reporting mechanism | Creator-side chat reporting; YouTube enforces |
| Respect parental / privilege settings | YouTube's restricted + supervised accounts |
| Remove/disable on request | YouTube removes; we never hosted the original |
| Disclose third-party UGC origin | Visibly YouTube chat; YouTube is platform of record |
And the enforcement is demonstrably real. YouTube's transparency report for Jan–Mar 2026: approximately 1.6 billion comments removed in a single quarter, with child safety the #2 removal reason at 124,624,293 (hateful/abusive 15,977,127; harassment/cyberbullying 71,326,861; spam/scams 1.31 B).
The certification-note sentence:
UGC is mirrored from YouTube, which moderates it at industrial scale upstream. We render transiently, persist nothing, and provide no user-to-user communication surface.
The "persist nothing" half is ours to keep — and it is the same property
task-49 (profanity filter) needs. Convenient, not coincidental.
Our read: 11.12 targets products hosting a UGC community. A chat mirror rendered from YouTube — where YouTube is the platform of record and already supplies reporting, age-gating, and parental controls — is arguably not "our" UGC. This is a judgment call, and we resolve it by documenting the position rather than building features on a guess. State the reasoning in the certification notes; Microsoft reads them.
The documentation we owe regardless
| Artifact | Requirement |
|---|---|
Privacy policy on llamachile.shop |
10.5.1. 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, nothing is fetched at runtime |
| Code of conduct + content guidelines | 11.12 / 11.15. General audience; explicitly not directed at under-13s |
| IARC age-rating questionnaire | 10.11.1 |
| Certification notes | the 11.12 reading, the YouTube age-gate argument, the Polar third-party purchase box, the demo account |
10. Camera, microphone, and "only when the user says so"
We do not write that certification. Windows does it — and the Store forbids us from bypassing it. Three layers:
- Policy 10.6 — "You must not circumvent operating system checks for capability usage." Route through the standard OS consent path; do not roll our own.
- Windows privacy settings — Settings → Privacy & security → Camera / Microphone. Since Windows 10 1903, "Let desktop apps access your camera/microphone" is the toggle that governs a Win32 broadcast app. Turn it off and the app loses access. That is the "explicit user direction" mechanism, enforced by the OS, not by our EULA.
- Hardware/OS indicators — camera light (or a notification when there is no light) and a microphone icon in the taskbar tray. Automatic. The OS is the disclosure mechanism, so we get neither credit nor blame from it.
⚠️ The open question — measure, don't assume
Microsoft's own support page carries this caveat:
Desktop apps may not always appear in the list of apps available to the Camera and Microphone settings pages or might still be able to access your camera or microphone even when these settings are turned off.
Untested: does the Win11 desktop-app camera toggle reliably gate a DShow webcam and a capture card?
- If it does gate them, we can make privacy-forward claims after verifying.
- If it does not, that is not a Store certification failure — we declared
webcamhonestly and use supported APIs — but it is a real user-trust issue, and we must not write privacy-forward marketing copy on the assumption that it works.
Tracked as an open item in TASKS.md. A guard note in MARCOM.md blocks privacy-forward
copy until it passes.
11. Open questions
- Individual or Company Partner Center account? (10.8.3 / 10.14.) Individual accounts are "usually appropriate for a single developer working on their own," and a Company account is required if the product requires financial account information for primary functionality. Polar collects payment externally and we never ask the user for card details, so we look like a clean individual case — but it is a question for Partner Center, and getting it wrong means re-enrolling.
- Does the desktop-app camera toggle gate DShow? (§10) — gates the
MARCOM.mdprivacy claim. - MSIX write behavior under a real package identity — full trust passes writes through per the docs, but worth confirming on a real packaged build before trusting it with recordings.
- Store revenue-share terms — do not plan on any specific percentage. The terms have moved more than once and small indie apps sometimes qualify for better rates. Verify current terms and conditions before pricing against them.
liveBroadcasts.insertrequiresstatus.selfDeclaredMadeForKids(COPPA) — already known, seeresearch-youtube-api.mditem 1. Confirm the dialog wording is right for a tool that is never child-directed.
12. Profanity filter — design constraints (feeds task-49)
YouTube's Community Guidelines carry a specific Vulgar language policy under "Sensitive content," alongside nudity/sexual content, child safety, and self-harm. So profanity is a named policy surface, not an edge case.
Three external reasons it earns a slot, plus the real one:
- YouTube treats it as policy-relevant, so 11.12's "proactive detection" language has something to attach to
- Competitive parity — overlay tools in this lane generally offer it
- It is a reasonable reading of 11.12's proactive-detection language
- The real reason: we control what gets projected on screen. That is the creator's call, not YouTube's.
Four constraints, because they protect the privacy story rather than complicate it:
- Local and on-device only. No server, no telemetry of message content, nothing leaves the machine.
- Non-persistent. Filter at ingest, discard the raw text, keep only the display string. Same property that makes the 11.12 answer in §9 strong.
- User-supplied word list, opt-in, stored locally. Not a hardcoded slur list baked into the binary — that is an unpleasant artifact, generates false positives across dialects and languages, and becomes our problem the moment someone extracts it. A user-curated list is also simply more useful: streamers know their community's euphemisms and we do not.
- Match display text only. Never the SuperChat amount or any reward-event field — those are financial data under 10.5.5, and matching on them would be a real mistake.
The existing alert box already has a ticker and three display methods
(Controls/OverlayHost.xaml, ViewModels/MainViewModel.Chat.cs), so there is probably a
single insertion point rather than scattered call sites. Verify before building.