Files
LlamaCasty/docs/ai/product.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

323 lines
22 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.
# Product: brand, licensing, monetization & distribution
What the product is called, what it promises, what it may never do legally, and how it ships.
Read before branding, marketing, pricing, licensing or packaging work.
## Brand
**The UI design standard is [`DESIGN.md`](../../DESIGN.md)** — colours, type scale, spacing, radii
and elevation, all normative, implemented by `Themes/Tokens.xaml`. Accent blue `#38bdf8` is the only
interactive accent; red `#ff5451` means live/record and nothing else. The old brand red `#e94560` is
retired from app chrome (the wordmark/logo artwork is unchanged).
Marketing palette, official assets and copy live in `MARCOM.md` (gitignored — read it before any
branding or marketing work).
### Product name vs repo/assembly branding (2026-09-14)
The product is **llamacasty** (llamacasty.com, llamachile.tube, `llgit.llamachile.tube`). The
repo path, csproj `AssemblyName`/`RootNamespace`, DB/log paths (`%APPDATA%\ytLlive\...`),
and most internal namings are the legacy **ytLive/ytLlive** — a re-brand that never renamed
the internals. Treat the two as separate: external/user-facing language says "llamacasty",
code/assembly/repo names stay `ytLive`. If a full internal re-brand is ever done, this note
and the UI locator (Premium/wordmark) are the checklists; it is NOT a goal pre-1.0.
**Say "LlamaCasty" when reporting to the creator** — never "ytLive" in prose. `ytLive` is
the internal repo/assembly/namespace name only. Exact current split:
| surface | name | note |
|---|---|---|
| product / site / marketing | **LlamaCasty** | llamacasty.com, llamachile.tube |
| repo dir, csproj, `RootNamespace` | `ytLive` | 230 files: `namespace ytLive.*` |
| `AssemblyName` → shipped exe | `ytLive` | `ytLive.exe` |
| app data dir | `%APPDATA%\ytLlive` | **capital L**, drifts from `ytLive` |
| `Distribution.md` tells customers | `LlamaCasty.exe` | ⚠ **mismatch vs the real `ytLive.exe`** |
**Consequences, so nobody re-hunts them:**
- Renaming `<AssemblyName>` to `LlamaCasty` is the cheap win (the customer-visible exe
name matches what `Distribution.md` already promises; namespaces can stay `ytLive.*`).
It is **4 lines**, but it hard-breaks three pack URIs that spell the assembly name:
`MainWindow.xaml:14` (the **window icon** — taskbar/Alt-Tab), `OverlayHost.xaml:96`,
`RenameRecordingDialog.xaml:5`. Miss those and you ship a broken icon. Queued in TASKS.md
under the shipping task, not done.
- Renaming the `%APPDATA%\ytLlive` folder is **breaking** — it would orphan every existing
user's layout DB, `Assets` and settings. Never do it casually; needs a migration.
- No code compares its own assembly name (no `GetExecutingAssembly().GetName().Name` checks),
so the assembly name is otherwise free to change.
### AI transparency
This project is built through AI-assisted pair-programming. All code is generated
under the supervision of an experienced developer with decades of coding experience.
Architecture decisions, product direction, and quality gates are human-owned;
AI accelerates implementation. This is a testimonial to successful human-AI
collaboration, not autonomous code generation.
## Licensing — proprietary since 2026-10-30
**LlamaCasty is closed-source commercial software.** `LICENSE` is the EULA; the paid tier exists
because output quality is the paid delta — the free tier is capped at a 720p30 profile (creator
ruling 2026-10-30, superseding free-forever). Prices, entitlement and channels:
[`commercial.md`](commercial.md).
- **Every GPL build already published stays GPL for that snapshot.** Attribution/§7 terms
(`ADDITIONAL-TERMS.md`) are deleted and were GPL-only; the files are gone.
- **No GPL third-party component may be linked.** ffmpeg stays a separate subprocess (LGPL,
relinkable via the shared build), OpenH264 ships as Cisco's binary, the rest are MIT/Apache/CC0.
Never add a statically linked GPL library — that would make the commercial build undistributable.
- **Obligations that still bind** (they are third-party, not ours): keep `THIRD-PARTY-NOTICES.txt`
shipped and shown in About, ship full licence texts at v1, keep the ffmpeg pin immutable and
`lgpl-shared`, never use the `nonfree` variant, keep icon art CC0/public-domain, keep the ML model
provenance clean (Google MediaPipe, Apache-2.0 — no model-zoo re-hosts).
- **Fonts:** Inter + JetBrains Mono are OFL-1.1, bundled as separate files with their licence text —
allowed in a commercial product, but the fonts may not be sold on their own and must not be
modified under the reserved names.
- **Brand artwork** (name, icon, logo) is the publisher's property; the EULA grants no trademark
rights and forbids implying endorsement.
- **No source offer.** The GPL §6 source link is gone; the repo is private.
## Design Principle
> This software is so intuitive that even the most right-brained person can easily intuit and use it.
Apply this to every UI decision:
- One-click go-live with working defaults
- Prefilled YouTube defaults (RTMP URL, bitrate, resolution, latency)
- Visual/drag-and-drop scene building over property panels
- Every action produces a visible outcome — no dead ends
- **"We're not them" (2026-09-01):** assume the creator's hardware is mediocre, because it is.
Every feature spends the machine's budget once — never twice for the same result (record OR
stream, not both; one reusable stream; one webcam; two mixer inputs). If a feature only sings on
a high-end chassis, it doesn't ship — the OBS escape hatch is open by design.
## Monetization — free tier + Pro output quality
**Every feature is free and ungated** (creator ruling 2026-10-08, TASK 54, kept by the 2026-10-30
freemium ruling): no feature lock, no watermark, and nothing licence-shaped in the output. The
paid delta is **output quality alone** — free is capped at a 720p30 profile, Pro unlocks the
higher tiers for **both the stream and the recording**. Prices, entitlement and channels:
[`commercial.md`](commercial.md). Support is email + GitHub issues; an in-app bug reporter is out
of product.
**The branding credit is retired as a paid delta and stays off.** `MainViewModel.ShowBrandFlash`
(internal static, default `false`) is the master switch; `BrandFlashEnabled => ShowBrandFlash &&
!IsPremium` feeds the presenter. The code is kept, not deleted — creator ruling: *"don't delete
it, just make it conditionally displayed."* Entitlement must never enable it.
`BrandFlashOutputTests` opts in by setting the switch before constructing a `MainWindow`;
`Credit_IsOffByDefault_EvenOnTheFreeTier` guards the default.
If it is ever enabled, the mechanics that matter:
- Full-frame neon "made with LlamaCasty!" at a **random position per presentation**, held **2s**
(500ms in / 1000ms full / 500ms out), first ~5s after start then every `rand(30s)+30s`.
- Published to the frame pump and the preview from the **same** `VideoFrame`, so preview and
broadcast cannot drift. Deliberately **not** folded into `SceneGraph.GetBakedBase` — the bake
cache compares element ids and would freeze the credit in place.
- **App-lifetime cadence:** the presenter is `Start()`ed once in the `MainViewModel` ctor, not per
go-live, because go-live and local recording share one `FramePump`. `IsPremium`'s setter therefore
restarts the cadence timer when re-enabling (`IsCadenceTimerEnabled` is the test seam).
- **It composites into the recording and the stream** (reversed 2026-09-26 — the free tier must be
honest advertising). Test arithmetic trap: `Advance(5.1)` in one call blows through the 2s window
(it both opens and ages the presentation) — step in 1/30s increments like `BrandFlashOutputTests`.
- **A mid-credit downgrade** cuts the advertisement on the next tick: the presenter re-checks its own
`Enabled` gate every frame rather than letting the credit finish.
Live prices, entitlement and channels: [`commercial.md`](commercial.md). Retired pricing, the
Store IAP plan and the rejected alternatives: [`decisions.md`](decisions.md).
**Monetization awareness (built-in, ungated, free — 2026-09-01; v1 SCOPE per the complete-v1
ruling — build order: capture → report → journey → Alerts):** the app is monetization-aware by
design, for **every** creator, free and ungated. It has three layers, all free — the capability no
competitor ships (the positioning order across features lives in `MARCOM.md`):
- **Reward-event capture (the data foundation).** The live-chat poll (`YouTubeChatService.Poll`)
receives every reward event over `liveChat/messages` and — since TASK 43 (2026-09-24) —
**decodes all six types**, not two: `superChatEvent`, `superStickerEvent`, `newSponsorEvent`,
`membershipGiftingEvent`, `giftMembershipReceivedEvent`, `memberMilestoneChatEvent` land as
`ChatMessage.Kind` (`ChatEventKind`) with their payload fields (display string, sticker
description, gift count, gifter name, milestone months); free subscriptions emit NO event
(API fact — a sub mention is a plain text row). The remaining half of the original capture
plan stays open (TASK 3 item 20): persist each event to the canonical SQLite `RewardEvents`
table (broadcastId, type, timestamp, amountMicros, currency, tier...) + `superChatEvents.list`
(30-day lookback) backfill — that persistence is what the session report and the journey
tracker read. Alerts did not wait on it: the shipped alert path (TASK 43) renders directly
from the in-memory `ChatMessage` feed.
- **Session / broadcast report.** A session is created at Go Live; the poll + End roll that session's
reward totals (new members, Super Chat $, stickers $, gifts, milestones). This is the in-app
replacement for the emailed external "stream activity report" — the incumbents (Streamlabs et al.)
email near-useless post-stream summaries; we track the same data natively and maintain it.
- **Journey tracker (TASK 39, slice 1 shipped 2026-09-22).** A **"YPP"** tab below the
Stream Settings pull-out (Live screen right edge) shows the connected creator's position
toward each YPP tier threshold. Slice 1 = current-scope data only: `channels.list?mine=true
&part=statistics,contentDetails` (subs/views/videos + uploads-playlist id) +
`playlistItems.list` for 90-day public uploads. Every refresh appends
a `YppSnapshot` to SQLite (schema v10) — the history a later slice needs for velocity/ETA.
**The `auditDetails` part is OUT (fixed 2026-09-23):** readding it 403s the WHOLE request —
that part alone requires the `youtubepartner-channel-audit` scope no app for normal creators
should request (the standing flags sat unreadable since slice 1 shipped; the app's `youtube` +
`youtube.force-ssl` scopes were never sufficient for them — the "no re-consent" slice-1 claim
was wrong for that part). Channel standing now deep-links to the Earn page; API stats come from
`statistics,contentDetails` only. YPP/API failures log the response body's `error.reason`.
**YPP thresholds are versioned, date-aware DATA, never constants** — the Tier-2 bar doubles
for new applicants on **2027-02-01** (long-form 4,000 → 8,000 qualified hrs / 365d; Shorts
10M → 20M / 90d), while the Tier-1 fan-funding bar (500 subs / 3,000 hrs / 3M Shorts) is
unchanged. Hardcoding the pre-2027 numbers would ship the wrong ETA. Watch-hours / Shorts /
velocity / ETA are **slice 2**: Analytics-API scopes (`yt-analytics.readonly`, additive
`yt-analytics-monetary.readonly`) behind the `IChannelStatsProvider` seam (additive
capability, requires an OAuth re-consent). Compliance items no API exposes (2FA, AdSense
linkage) are in-app self-reported checkboxes with deep links; channel standing (exposed only
via the partner-audit scope) deep-links to the Earn page. Slice 2's once-daily refresh gate +
snapshot history were baked into slice 1.
## Auth gates Go Live, but not exploration
The app is fully usable without authentication: users can build scenes, add sources, compose
previews, and audition the software with zero commitment. But **going live requires authentication** —
it's the one capability gated behind YouTube sign-in. The sign-in should never pressure the user
("sign in (optional)", not a modal wall): the two-state top bar shows **Start Stream** (offline) /
**End Stream** (live), and the Start Stream dialog hosts the account — a saved session appears as
the default with "Change Account"; with none saved, a "Sign in to YouTube" button starts OAuth and
the Start button stays disabled until signed in.
## Account assumption (do not build an account setup flow)
Connecting uses Google OAuth ("Sign in with Google") to link an **existing** YouTube creator
account. ytLlive **never creates or sets up accounts** — that is YouTube's job. If the creator has no
YouTube channel, they go to YouTube first. This assumption is explicit and must never be silently
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)
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:
[`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).
**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".
- **⛔ 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.
**What does NOT change: no watermark and no feature gating.** The free tier is the whole app at a
720p30 profile. `Monetization` above governs.
### ⛔ Durable invariant: full trust, or recording silently breaks
MSIX packages run at one of two trust levels. A packaged WPF app declaring
`rescap:Capability Name="runFullTrust"` runs at **mediumIL — the same permissions as a
standard desktop app, NOT AppContainer** — and may spawn child processes, which is why
bundling ffmpeg is legal. Writes to user-profile locations (AppData, Downloads, MyVideos)
**pass through unvirtualized at mediumIL**; the package install folder is read-only.
**`ViewModels/MainViewModel.Recording.cs` writes recordings to
`%USERPROFILE%\Downloads` / `SpecialFolder.MyVideos` (`DefaultRecordFolder()`,
`ChooseRecordFolder()` via `Microsoft.Win32.OpenFolderDialog`), and the whole
temp-write-then-move lifecycle stays in one directory.** That works *only* because we are full
trust. No `broadFileSystemAccess` capability is needed, and none should be added.
⚠️ **If anyone flips the manifest to `appContainer`, `Directory.CreateDirectory` and
`File.Move` start resolving to a per-package virtualized location and output vanishes with no
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
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.
- **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).
### 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.
### ⛔ "Only when the user says so" is enforced by Windows, not by us
Camera/mic consent is layered: Store policy **10.6** forbids circumventing OS checks; the
**Windows desktop-app camera/microphone toggle** (Settings → Privacy & security, since Win10
1903) is the gate; hardware lights and tray indicators are the OS's disclosure mechanism. So
**route everything through the standard MF/DShow/WASAPI paths and we never write that
certification ourselves** — the OS does it and gets neither our credit nor our blame.
⚠️ **Untested:** whether that toggle reliably gates a **DShow webcam and a capture card**.
Microsoft's own support page admits desktop apps "might still be able to access your camera
or microphone even when these settings are turned off." This is **not** a certification
failure (we declare `webcam` honestly and use supported APIs) but it is a real user-trust
issue — so **no privacy-forward marketing copy until it is measured.** Guard note added to
`MARCOM.md`.
### ⛔ Durable invariant: chat is rendered, never stored
`TASK 49`'s profanity filter is **local, on-device, opt-in, non-persistent**, with a
**user-supplied word list — never a hardcoded slur list baked into the binary** (unpleasant
artifact, false positives across dialects, extractable). It must **never match the SuperChat
amount or any reward field** — those are financial data under policy 10.5.5.
The non-persistence half is not just hygiene: it is the load-bearing half of the **11.12 UGC
certification answer** (mirrored YouTube chat is moderated at industrial scale upstream; ~1.6B
comments removed in Q1 2026 alone, child safety the #2 reason at 124.6M). **No future feature
— chat history, moderation logs, analytics, crash payloads — may quietly break that
assumption.** If one is ever proposed, the 11.12 position must be re-argued first.
### Minors
The **operator is 13+ by construction**, not by promise: LlamaCasty requires a YouTube
account to stream, and YouTube's ToS sets the account floor at **13** (regional variants reach
14; 13–17 need parent/guardian permission; under-13s are on supervised/YouTube Kids accounts).
So a COPPA theory premised on collecting PI from under-13s has no purchase against the
operator. The residual is **viewers** — still possible on supervised family accounts — and it
is weak: live chat is restricted there, YouTube moderates before we see it, and we persist
nothing. COPPA is very unlikely to bind (not child-directed), but note the enforcement trend
(FTC Sept 2025 *Apitor*: operators are responsible for what third-party components collect) —
so the answer is a documented inquiry, and ours is "no third-party analytics or ad SDKs, no
persisted chat or reward payloads."