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.
111 lines
6.3 KiB
Markdown
111 lines
6.3 KiB
Markdown
# Distribution & release mechanics
|
||
|
||
How the build becomes an install on someone's PC. **Policy — prices, entitlement, why Steam is the
|
||
only channel, support — is [`docs/ai/commercial.md`](docs/ai/commercial.md).** This file is the how.
|
||
|
||
> Superseded twice on 2026-10-31. The 736-line Polar/MSIX/obfuscation plan is in
|
||
> `git log -- Distribution.md`; the zip-plus-winget plan written earlier the same day is in the
|
||
> same place. The reasoning that survives lives in `commercial.md` and `docs/ai/decisions.md`.
|
||
|
||
## 1. The artifact
|
||
|
||
A self-contained win-x64 publish plus the bundled `ffmpeg\` folder, uploaded to a Steam depot. No
|
||
installer, no admin rights, no zip for anyone to mirror.
|
||
|
||
```bash
|
||
dotnet publish ytLive.csproj -c Release -r win-x64 --self-contained true \
|
||
-p:PublishSingleFile=true \
|
||
-p:IncludeNativeLibrariesForSelfExtract=true \
|
||
-p:EnableCompressionInSingleFile=true \
|
||
-p:PublishReadyToRun=true -o artifacts/steam-stage
|
||
```
|
||
|
||
- **`PublishTrimmed` stays OFF.** Trimming breaks WPF's XAML reflection (`Binding`, `DataTemplate`,
|
||
`StaticResource`). Expect ~110–150 MB in the depot; that is the cost of "no .NET install".
|
||
- **ffmpeg stays outside the exe** — `ffmpeg\` beside it (the app's locator contract, see
|
||
[`output.md`](output.md) → "FFmpeg locator").
|
||
- **`steam_api64.dll` ships beside the exe** and is loaded at startup; without it the app cannot
|
||
read DLC ownership. Its absence must degrade to the free profile, never to a crash.
|
||
- **WebView2 is the one machine dependency.** Valve's Common Redistributables configure the
|
||
Evergreen bootstrapper as a prerequisite, so the depot declares it instead of us shipping it.
|
||
- **`AssemblyName` is still `ytLive`** → the exe is `ytLive.exe` today. The rename to
|
||
`LlamaCasty.exe` is queued (three pack URIs break — see `product.md`), and **must land before the
|
||
first depot upload**, because Steam's docs are explicit that renaming a live exe is painful.
|
||
- **Portable mode** (`portable.txt` → `.\data\`) survives as a feature for a copied install; it is
|
||
not a delivery vector.
|
||
|
||
## 2. Steamworks setup, once
|
||
|
||
1. **Steamworks account** → onboarding (NDA, identity, company details, tax interview, payout bank,
|
||
then the **$100 Steam Direct fee** per app). The store page cannot be configured before the fee.
|
||
2. **App IDs.** One **base app** (free) and one **DLC app** (the lifetime Pro). Note both numbers:
|
||
the DLC app ID is what the app passes to `BIsDlcInstalled`.
|
||
3. **Depot.** One depot holding the publish output; the base app and the DLC share the install
|
||
directory, and the DLC package grants no files of its own — it is entitlement only.
|
||
4. **Steamworks SDK.** Drop `steam_api64.dll` (+ `steam_appid.txt` for local runs) into the publish
|
||
output; the managed side is a thin wrapper (`Steamworks.NET` or hand-written interop) behind
|
||
`IEntitlement`.
|
||
5. **Store page** configured and made public **two weeks before release** — Valve's rule, and it is
|
||
the window where wishlists accumulate.
|
||
|
||
## 3. Release
|
||
|
||
```
|
||
publish → steam-stage → steamcmd + app_build.vdf → build upload → set live → announcement
|
||
```
|
||
|
||
1. **Upload the depot** with `steamcmd +login <user> +run_app_build ..\scripts\app_build_<appid>.vdf
|
||
+quit`. The VDF names the app, depot, content root and branch.
|
||
2. **Set the build live** in Steamworks → *Builds* → the new build → **Set Build Live** on the
|
||
default branch. That *is* the release; there is no artifact to hash and no manifest to submit.
|
||
3. **Announce** with Steam Events (release notes appear in the library and on the store page).
|
||
4. **Verify**: install from a clean Steam account, confirm `BIsDlcInstalled` returns false, buy the
|
||
DLC (or use a test licence), confirm the high tiers unlock, then refund the test purchase.
|
||
|
||
**Updates** are the same upload with a higher build ID. The app keeps **no self-updater and no
|
||
version banner** — Steam is the update path, and that policy has not changed.
|
||
|
||
## 4. Entitlement wiring
|
||
|
||
- `IEntitlement` has two implementations: **Steam DLC** (lifetime, local) and **Polar** (monthly,
|
||
network + 7–14 day lease). Resolution is `Pro = steamDlc || polarValid`; anything unresolved is
|
||
the free profile.
|
||
- **Never sell the monthly inside the Steam build.** Steamworks requires in-app purchases to use
|
||
the Steam Wallet; the app may *state* that monthly plans exist on the site, with no checkout,
|
||
no price and no payment field. Confirm the wording with Steamworks support before shipping it.
|
||
- **Never gate a feature.** The only delta is the output profile.
|
||
|
||
## 5. Release checklist
|
||
|
||
- Build clean, 0 warnings (`./scripts/verify.sh`), tests green.
|
||
- `steam_api64.dll` present; app starts **without** Steam and falls back to the free profile.
|
||
- DLC ownership false → free profile; true → every tier offered.
|
||
- `LlamaCasty.exe` rename landed (never rename a live exe).
|
||
- Clean Steam install on a second machine, then an update pushed and observed.
|
||
- Store page public for the full two weeks; support contact and announcements configured.
|
||
- Portable mode: `portable.txt` present → data lands in `.\data\`.
|
||
|
||
## 6. Legal and takedown
|
||
|
||
- **`LICENSE`** (EULA) forbids redistribution, resale, reverse-engineering and circumvention, and
|
||
disclaims warranty.
|
||
- **`PRIVACY.md`** must name any third party the app contacts — Google/YouTube today, **Polar** the
|
||
day licence validation ships. Steam itself is named by Valve's own policy, not ours.
|
||
- **`THIRD-PARTY-NOTICES.txt`** stays shipped and shown in About; the ffmpeg pin stays
|
||
`lgpl-shared`; no GPL component may be linked (see `product.md` → "Licensing").
|
||
- **DMCA**: a takedown needs only the notice — host, link, good-faith statement, signature.
|
||
Statutory damages need a registration ($65). Worth doing once there is something to pirate.
|
||
- **What actually protects the product** is in `commercial.md` → "Anti-piracy posture": a free tier
|
||
good enough that a crack buys nothing, Steam's own delivery, short Polar leases, and a licence ID
|
||
in About. **Not obfuscation, not a native DLL, not hardware locking.**
|
||
|
||
## 7. Effort, once
|
||
|
||
| Step | Effort |
|
||
|---|---|
|
||
| Steamworks onboarding + $100 fee | 0.5–1 day, mostly waiting on verification |
|
||
| Publish profile + `steam_api64.dll` + `IEntitlement` Steam path | 1 day |
|
||
| Depot + first `steamcmd` upload | 0.5 day, then ~15 min per release |
|
||
| Store page + assets (screenshots, capsule art) | 1–2 days |
|
||
| Clean-account install + DLC purchase/refund test | 0.5 day |
|