Files
LlamaCasty/Distribution.md
T
gramps 57ef9e7a5e docs(commercial): the claim page was never needed, signing waits for revenue, and support is a filter
Four things derived this session superseded what the plan said twenty-four hours earlier,
and the docs are corrected where each one died:

**Licensing needs no server.** Polar's customer-portal endpoints authenticate on the key
itself plus the public organization_id; the org access token is only needed to create
products. Channel binding rides in `conditions: {channel_id}`, re-checked on every
validate — so the "claim page" (a hosted OAuth flow that wrote the UC id onto the key)
and the key database it implied are deleted. Device management, key rotation and
subscription auto-revocation are Polar's; four states, one lease, no callback endpoint.

**Signing is deferred until two subscribers.** It never removes the day-one SmartScreen
warning (EV's instant-bypass died in 2024), so buying it before revenue buys only the
publisher line. Renamed to Azure Artifact Signing, $9.99/mo Basic, and it requires a
paid Azure subscription. Year-one fixed cost drops from ~$135 to ~$15.

**The Store account is free.** Microsoft waived the $19 individual fee and removed the
$99 company fee in May 2026, which makes the Store the only zero-cost route to a
Microsoft-signed install — the free answer to SmartScreen, noted beside the paid one.

**Support is a filter, not a promise.** Email, best effort, no SLA, refunds on request
without argument; no training service, no helpdesk. A repeated question becomes a
training video on @llamacasty, which is content and monetization instead of a ticket.
GitHub issues on the releases repo are the deliberate friction.

Also settled: binaries live in a second Gitea repo (`llamacasty-releases`) push-mirrored
to a public GitHub repo — issues and assets public, code private, one publish direction
(a git mirror carries refs, not GitHub Release assets); `@llamacasty` is
standard-verified with intermediate features, so live streaming, >15 min uploads and
custom thumbnails work today and no SIM or ID is needed; and OAuth comes out of Testing
mode by publishing the app (100-user lifetime cap, no 7-day token death).

TASKS.md now opens with the four-day surge slice — artifact shape, output gate,
entitlement seam, real-machine day — because those are what the first public release
waits on. HANDOFF.md rewritten. MyMistakes.md gains the Polar recipe and the
"verify means three different things" entry. MARCOM.md gains the support→content loop
and the surface roles.
2026-10-31 10:08:05 -07:00

154 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Distribution & release mechanics
How the build becomes a file someone installs. **Policy — prices, entitlement, which channels
exist and why — is [`docs/ai/commercial.md`](docs/ai/commercial.md).** This file is the how.
> The previous version of this document was a 736-line Polar/MSIX/obfuscation plan. It was
> superseded on 2026-10-31; the reasoning that still matters lives in `commercial.md` and
> `docs/ai/decisions.md`, and the full text is in `git log -- Distribution.md`.
## 1. The artifact
One self-contained single-file exe plus the bundled ffmpeg folder, zipped. No .NET install on the
target machine, no installer framework, no admin rights.
```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
```
- **`PublishTrimmed` stays OFF.** Trimming breaks WPF's XAML reflection (`Binding`, `DataTemplate`,
`StaticResource`). The exe is therefore fat — expect ~110–150 MB compressed with the runtime
inside, and that is the accepted cost of "copy it and run it".
- **ffmpeg stays outside the exe** — `ffmpeg\` beside it (already the app's locator contract, see
[`output.md`](output.md) → "FFmpeg locator"). Embedding it passes 200 MB and adds an extraction
step for no benefit.
- **WebView2 is the one machine dependency**: the Evergreen runtime ships with Windows 11 and most
Windows 10; a genuinely clean machine needs the bootstrapper once. Say so in the download note.
- **`AssemblyName` is still `ytLive`** → the shipped exe is `ytLive.exe` today. The rename to
`LlamaCasty.exe` is queued (three pack URIs break — see `product.md`), and until it lands every
release note names the real file.
**Portable mode.** A `portable.txt` marker beside the exe keeps the layout DB, `Assets` and
settings in `.\data\` instead of `%APPDATA%\ytLlive` (hung off `LayoutPathOverride`). Same binary,
USB-stick behaviour, nothing to uninstall.
## 2. Where releases live — the public handout repo
**The source repo stays private.** Releases live in a **second Gitea repo,
`llamacasty-releases`**, which is push-mirrored to a **public GitHub repo** (`llamacasty/releases`).
That public repo holds nothing but a README, an issue template, a known-issues page and the release
assets — so the only thing that can ever leak is content authored for publication.
Why it is shaped that way: GitHub has no "public issues on a private repo" switch, and issue
visibility follows repo visibility. An empty public repo gives a public issue tracker, a durable
binary URL for winget, and `releases/latest` for the update notice, without the code going anywhere.
**The mirror carries refs, not release assets.** Git mirrors branches and tags; GitHub Release
uploads live outside the object store, so the zip is uploaded separately.
**Setup, once:**
1. Gitea → new repo `llamacasty-releases` (private) with `README.md`,
`.github/ISSUE_TEMPLATE/bug.yml`, `known-issues.md`.
2. Gitea → that repo → **Settings → Mirror Settings → push mirror** →
`https://github.com/llamacasty/releases.git` + a GitHub PAT. One-way and force-updating, so the
public copy can never drift from what was authored.
3. Create the free GitHub **organisation** `llamacasty` so URLs aren't tied to a personal account.
4. Point the Steam store page's support URL at
`https://github.com/llamacasty/releases/issues/new/choose` — the template chooser, never the
open-issue list.
**Rejected:** a submodule (publishes a pointer, not files), `git subtree split` (three static files
don't justify the history surgery, and one stray private file in the prefix leaks forever), and
serving zips from the droplet (uses the VPS as a CDN and makes the update notice hand-rolled).
## 3. Release
```
publish → zip → SHA-256 → gh release create → winget manifest PR → update notice
```
1. **Tag and publish.** `v<major>.<minor>.<patch>` tag; upload
`LlamaCasty-<version>-win-x64.zip` with
`gh release create v1.0.0 LlamaCasty-1.0.0-win-x64.zip --repo llamacasty/releases
--notes-file RELEASE.md`. Binaries live there, never on the droplet — free CDN, durable URLs,
release notes in the same place, and `wingetcreate` reads `releases/latest` natively.
2. **Record the hash.** `sha256sum` the zip; it goes in the winget manifest and in the release
notes. This is the anti-tamper wall: the official channel delivers exactly this artifact or
fails.
3. **winget.** `wingetcreate update LlamaCasty.LlamaCasty --version <v> --urls <release-url>
--submit` — it opens the PR against `microsoft/winget-pkgs`. Manifest shape:
`InstallerType: portable`, `InstallerSha256`, `NestedInstallerType` where the zip nests the exe.
First submission is `wingetcreate new`; the identifier is `LlamaCasty.LlamaCasty`.
4. **Update notice.** Commit the current version into the releases repo (so it arrives in the same
push or a follow-up commit) or read `releases/latest` from the GitHub API. The app shows one
line — *"v1.4 is out — `winget upgrade LlamaCasty.LlamaCasty`"*. **Nothing downloads or
executes**: that is Store policy 10.2.2 and a permanent no (see `product.md`).
5. **Optional mirrors.** Scoop (a small JSON manifest hosted in a bucket) and Chocolatey (a nupkg
with a checksum) — ten minutes each, same audience.
6. **MSIX** stays buildable for the parked Store channel: `scripts/package-msix.sh` →
`artifacts/LlamaCasty.msix`, submitted nowhere until the (now free) developer account exists.
## 4. Signing — deferred until revenue
**Unsigned is the launch state.** Artifact Signing is bought at **two subscribers**, not before:
it never removes the day-one warning (EV's SmartScreen bypass died in March 2024), so the only
thing it changes early is the publisher line. Costs and posture:
[`docs/ai/commercial.md`](docs/ai/commercial.md).
- **Azure Artifact Signing** (renamed from "Trusted Signing") — **$9.99/month Basic**, 5,000
signatures, **requires a paid Azure subscription**. Individual identity validation is the slow
part (days to weeks) and must be renewed; start it when the purchase is decided.
- **⛔ No EV certificate, and no certificate with an HSM.** Microsoft removed the EV instant-bypass
in March 2024, so EV ($400+/yr) buys exactly what OV (~$150–300/yr) buys.
- **The free alternative is the Microsoft Store**: the individual developer fee was waived, and
Microsoft signs the MSIX, so a Store install never meets SmartScreen at all.
- **Unsigned costs a warning on every release**, because SmartScreen ties reputation to the file
hash. Signing accrues reputation to the **publisher** — the only exit from that loop.
- **Signing never clears the warning on day one either.** Plan for weeks.
- Secrets: the Trusted Signing profile lives in CI secrets; **never commit** a `.pfx` or
`metadata.json` with credentials. `MSIX_PFX`/`MSIX_PFX_PASSWORD` are for local test installs only.
- Verify every release: `signtool verify /pa /v <file>`, and confirm the hash in the manifest
matches the uploaded zip.
## 5. Release checklist
- Build clean, 0 warnings (`./scripts/verify.sh`), full test suite green.
- `signtool verify /pa` passes on the shipped exe.
- Launch on a clean Windows profile: first-run works, recording writes where it claims, ffmpeg is
found beside the exe, WebView2-loaded UI renders.
- Fresh install through `winget install LlamaCasty.LlamaCasty` on a second machine, then
`winget upgrade` over the previous version.
- ZIP hash published; manifest PR merged; version notice updated.
- Portable mode: `portable.txt` present → data lands in `.\data\`, deleting the folder deletes the
app's footprint.
## 6. Legal and takedown
- **`LICENSE`** (EULA) forbids redistribution, resale, reverse-engineering and circumvention, and
disclaims warranty. It is what makes a mirror site a licence breach rather than a grey area.
- **`PRIVACY.md`** must name any third party the app contacts — today that is Google/YouTube,
ffmpeg is local, and **Polar** is added the day licence validation ships.
- **`THIRD-PARTY-NOTICES.txt`** stays shipped and shown in About; the ffmpeg pin stays
`lgpl-shared`, and no GPL component may be linked (see `product.md` → "Licensing").
- **DMCA**: statutory damages need a registration ($65), and a takedown needs only the notice —
host, link, good-faith statement, signature. Worth doing once there is something to pirate.
- **What actually protects the product** is in `commercial.md` → "Anti-piracy posture": signed
hash-pinned delivery, short leases, a free tier good enough that a crack buys nothing, and a
licence ID in About. **Not obfuscation, not a native DLL, not hardware locking.**
## 7. Effort, once
| Step | Effort |
|---|---|
| Publish profile + portable mode | 0.5 day |
| Releases repo + Gitea→GitHub push mirror | 0.5 day |
| `gh release` + hash + version notice | 0.5 day |
| First winget manifest (`wingetcreate new`) | 0.5 day, then ~15 min per release |
| Clean-machine + `winget` smoke test | 0.5 day |
| Artifact Signing profile + CI step (deferred) | 0.5 day, at two subscribers |