docs(distribution): Steam is the only channel — the zip, winget and the Store are dead
The site said "Coming soon to Steam" while the app docs still said direct download plus winget primary with Steam parked. One of them had to move, and the creator's ruling is Steam exclusive: a free app on Steam with the paid edition as a Steam DLC, and the $10/month plan sold off-Steam by Polar. Records the constraint that shapes the code, not just the prose: - Steamworks requires in-app purchases to use the Steam Wallet (Microtransactions doc, quoted in commercial.md), so the monthly is *stated*, never *sold*, inside a Steam build; the lifetime is plain DLC bought on the store page and read with BIsDlcInstalled. - Entitlement is `steamDlcOwned || polarKeyValid`, both resolving to one OutputProfile. steam_api64.dll ships beside the exe and is a functional dependency, not the rejected "IP protection" native DLL — without it DLC ownership cannot be read. Launched without Steam, the app falls back to the free profile rather than refusing to start. - Signing is moot: Steam installs never meet SmartScreen. Artifact Signing stays on the shelf for the day a non-Steam download exists. Year-one fixed cost is ~$115. - The exe rename must land before the first depot upload — Steamworks warns about renaming a live exe. Distribution.md is rewritten to SteamPipe mechanics (publish output, depot, steamcmd, DLC wiring, clean-account purchase/refund test). product.md, decisions.md (a new "Steam exclusive" entry with every dead vector), TASKS.md (Steamworks entitlement row, Steam packaging row), ai.md, index.md and the TASK 48 banner follow. MyMistakes.md gains the Steam rules that cost real time today: installs skip SmartScreen, updates are free, the Wallet rule, and the recurring-billing caveat.
This commit is contained in:
+37
-49
@@ -189,46 +189,40 @@ YouTube channel, they go to YouTube first. This assumption is explicit and must
|
||||
replaced by an in-app account-creation step. Zero state = the Start Stream dialog's "Sign in to
|
||||
YouTube" button; going live is unreachable until an account is connected.
|
||||
|
||||
## Windows packaging & distribution (winget primary; Steam + Store parked)
|
||||
## Windows packaging & distribution (Steam only)
|
||||
|
||||
Live channel policy, the artifact and signing costs: [`commercial.md`](commercial.md). Build
|
||||
mechanics (publish flags, the releases repo mirror, the winget manifest):
|
||||
[`Distribution.md`](../../Distribution.md). Retired routes and cause of death:
|
||||
Live channel policy, the artifact and costs: [`commercial.md`](commercial.md). Build and depot
|
||||
mechanics: [`Distribution.md`](../../Distribution.md). Retired routes and cause of death:
|
||||
[`decisions.md`](decisions.md).
|
||||
|
||||
**The channels, in order:** direct download from `llamacasty.com` (binaries in a separate
|
||||
`llamacasty-releases` repo, push-mirrored to public GitHub) → **winget**, the primary install path →
|
||||
Steam (parked, $100, lifetime Pro as DLC only; the only channel with discovery *and* an install
|
||||
that never meets SmartScreen) → Microsoft Store (parked, **free** account since the $19 individual
|
||||
fee was waived — the only zero-cost route to a Microsoft-signed install).
|
||||
**One vector: Steam.** A free app on Steam with the paid edition as a Steam **DLC**; the monthly
|
||||
plan is sold outside Steam and is never purchasable inside a Steam build (Steamworks requires the
|
||||
Steam Wallet for in-app purchases). No direct download, no winget, no Microsoft Store.
|
||||
|
||||
**Consequences that are architecture, not packaging detail:**
|
||||
|
||||
- **⛔ There is no self-updater, by policy as well as by history.** Velopack was removed on
|
||||
2026-10-22 (TASK 48 slice 3) because it downloaded *and executed*, which Store policy 10.2.2
|
||||
forbids — and the same rule would close the Store for good if it came back. Updates arrive
|
||||
through the package manager (`winget upgrade`) plus an in-app "newer version exists" banner read
|
||||
from a version file in the releases repo. `ytLive.Tests/StorePackagingTests.cs` must keep
|
||||
asserting that no self-updater returns.
|
||||
- **The MSIX package stays buildable** via `scripts/package-msix.sh` (TASK 48 slice 2) — publish
|
||||
`Release -r win-x64 --self-contained` into `artifacts/msix-stage`, copy
|
||||
`Packaging/Package.appxmanifest` in as `AppxManifest.xml` + `Packaging/Assets/`, then
|
||||
`makeappx pack` → `artifacts/LlamaCasty.msix` (159 MB, gitignored; `MSIX_PFX` signs it for a
|
||||
local install). It is the Store channel's artifact, unsubmitted until that account exists. The
|
||||
manifest is a **full-trust `packagedClassicApp` at `mediumIL`** with `runFullTrust` + webcam +
|
||||
microphone. `Square310x310Logo`/`Wide150x150Logo` traps are in `MyMistakes.md`. Logos come from
|
||||
the app's own artwork via `scripts/make-store-logos.py`.
|
||||
- **⛔ Bundle ffmpeg — no runtime downloads** (was Store policy 10.2.2; still right for a direct
|
||||
download, and it fixes the 2026-09-01 404). See [`output.md`](output.md) → "FFmpeg locator".
|
||||
- **⛔ There is no self-updater, and now no version banner either.** Steam delivers updates; the
|
||||
app keeps nothing that downloads or executes, for the same Store-policy reason it always had.
|
||||
`ytLive.Tests/StorePackagingTests.cs` must keep asserting that no self-updater returns.
|
||||
- **⛔ `steam_api64.dll` is a functional dependency**, not the rejected "native helper DLL for IP
|
||||
protection" — Steam DLC ownership cannot be read without it. The app must start without Steam
|
||||
running and fall back to the free profile.
|
||||
- **The MSIX package stays buildable but is now unused** — `scripts/package-msix.sh` (TASK 48
|
||||
slice 2) still produces `artifacts/LlamaCasty.msix` from `Packaging/Package.appxmanifest` +
|
||||
`Packaging/Assets/`. It was the Store channel's artifact; with Steam exclusive there is no
|
||||
submission target. The full-trust `packagedClassicApp` findings below remain true and are still
|
||||
worth reading before any packaged build is attempted.
|
||||
- **⛔ Bundle ffmpeg — no runtime downloads** (was Store policy 10.2.2; still right for a Steam
|
||||
depot, and it fixes the 2026-09-01 404). See [`output.md`](output.md) → "FFmpeg locator".
|
||||
- **⛔ 64-bit Windows only (creator ruling 2026-10-09).** Both csproj files set
|
||||
`<PlatformTarget>x64</PlatformTarget>` (the test project too — an AnyCPU test project
|
||||
referencing an x64 app raises MSB3270 and breaks the 0-warning gate). Why: the bundled ffmpeg
|
||||
is the win64 build, ONNX Runtime 1.31 ships no win-x86 native (only win-x64/win-arm64), and a
|
||||
32-bit address space can't hold the 1080p frame buffers. Windows 11 is 64-bit-only anyway.
|
||||
Public copy says "64-bit Windows 10 or 11".
|
||||
- **Portable mode is the distributable's shape:** `portable.txt` beside the exe keeps the layout DB
|
||||
and settings in `.\data\` (hung off `LayoutPathOverride`), so a USB stick carries someone's
|
||||
scenes. The `winget` install and the hand-to-a-friend zip are the same artifact.
|
||||
- **Portable mode is a feature, not a channel:** `portable.txt` beside the exe keeps the layout DB
|
||||
and settings in `.\data\` (hung off `LayoutPathOverride`), so a copied install carries someone's
|
||||
scenes. Steam installs it; the marker is for afterwards.
|
||||
|
||||
**What does NOT change: no watermark and no feature gating.** The free tier is the whole app at a
|
||||
720p30 profile. `Monetization` above governs.
|
||||
@@ -252,32 +246,26 @@ trust. No `broadFileSystemAccess` capability is needed, and none should be added
|
||||
error.** A comment at `DefaultRecordFolder()` is owed **when the manifest lands** (TASK 48
|
||||
item 6) — not before; a comment describing a manifest that doesn't exist is worse than none.
|
||||
|
||||
### Signing — deferred until revenue; Azure Artifact Signing, never EV
|
||||
### Signing — not needed, because Steam installs the app
|
||||
|
||||
Microsoft **removed the SmartScreen instant-bypass for EV certificates in March 2024**, so EV
|
||||
($400+/yr) buys what OV (~$150–300/yr) buys; both need an HSM or hardware token and renew inside
|
||||
**460 days**. The 2026-10-31 ruling was **Azure Trusted Signing (~$9.99/month)**; the later ruling
|
||||
the same week is that it is **deferred until two subscribers exist**, because it never removes the
|
||||
warning on day one — only download volume does — and the storefront alternative is now free.
|
||||
Steam's client delivers the files, so there is no browser download, no Mark-of-the-Web and no
|
||||
SmartScreen wall. **The certificate has no job in this channel.** It stays on the shelf for the day
|
||||
a non-Steam download exists:
|
||||
|
||||
- **Renamed**: it is **Azure Artifact Signing** now. Basic is $9.99/month, 5,000 signatures, and it
|
||||
**requires a paid Azure subscription**; individual identity validation exists and must be
|
||||
renewed, and it is the slow (days-to-weeks) part — start it when the purchase is decided.
|
||||
- **Unsigned is a per-release tax**: SmartScreen ties reputation to the file hash, so an unsigned
|
||||
product warns on **every** release, forever. Signing is the only exit from that loop.
|
||||
- **The free exit is the Microsoft Store** — the individual developer account fee was waived, and
|
||||
Microsoft signs the MSIX, so a Store install never meets the wall.
|
||||
- **Never EV, never a certificate with an HSM**, for this product.
|
||||
Signing steps: [`Distribution.md`](../../Distribution.md). Costs: [`commercial.md`](commercial.md).
|
||||
- **Azure Artifact Signing** (renamed from "Trusted Signing") — $9.99/month Basic, up to 5,000
|
||||
signatures, **requires a paid Azure subscription**; individual identity validation is the slow
|
||||
part and must be renewed.
|
||||
- **Never EV** — Microsoft removed the EV SmartScreen instant-bypass in March 2024, so EV buys what
|
||||
OV buys. **Never a certificate with an HSM** for this product.
|
||||
- **Steam does not sign the exe.** A UAC prompt for the app is elevation, not publisher trust.
|
||||
|
||||
### Distribution and licensing are independent — the channel never decides billing
|
||||
|
||||
A **Store-distributed** app *may* still verify licences with **Polar** (Store policy 10.8.1
|
||||
explicitly permits a secure third-party purchase API for non-game PC products), so "which
|
||||
channel?" never implies "which billing?". **Polar is the live provider** (2026-10-31) because the
|
||||
2026-10-30 freemium ruling created something to sell; winget has no storefront at all, so the
|
||||
licence path is entirely ours there. The lesson worth keeping: the licensing choice rides on a
|
||||
**monetization** decision, not on a distribution decision.
|
||||
The Steam build sells the lifetime as **Steam DLC** and reads it with `BIsDlcInstalled`, while the
|
||||
monthly plan is billed by **Polar** outside Steam. Steamworks forbids taking payment inside the app
|
||||
with anything but the Steam Wallet, so the app states where the monthly lives and never sells it.
|
||||
The lesson worth keeping: the licensing choice rides on a **monetization** decision, not on a
|
||||
distribution decision.
|
||||
|
||||
### ⛔ "Only when the user says so" is enforced by Windows, not by us
|
||||
|
||||
|
||||
Reference in New Issue
Block a user