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.
219 lines
13 KiB
Markdown
219 lines
13 KiB
Markdown
# Commercial: pricing, entitlement, channels, support
|
||
|
||
The single source of truth for what LlamaCasty charges, what a licence entitles, how it ships and
|
||
what support it does and does not include. Read before any pricing, licensing, distribution,
|
||
storefront, support or promo work. Cause of death for every retired path:
|
||
[`decisions.md`](decisions.md). Release mechanics (build, depot, SteamPipe upload):
|
||
[`Distribution.md`](../../Distribution.md).
|
||
|
||
## Model
|
||
|
||
- **Free tier — every feature, every scene, every overlay**, free forever, no account, no
|
||
watermark, nothing nagged inside the output. It is not a trial.
|
||
- **Pro — $10/month or $400 lifetime.** No annual tier.
|
||
- **The only delta is output quality.** Features are never gated; a payment buys pixels.
|
||
- **No watermark, ever**, in either edition. The branding flash is retired as a paid delta and
|
||
stays off (`ShowBrandFlash`, default `false`).
|
||
- **Clean numbers, no `.99`**, no discount codes, no countdowns, no fake scarcity.
|
||
- **The site publishes the model, never the numbers** — "free forever, every feature unlocked, a
|
||
paid edition with higher output quality at launch". Price, tier name, cap and frame rate are
|
||
published by the **app**, at the point of sale, not by the page.
|
||
|
||
## Output profile — the one entitlement rule
|
||
|
||
The cap is a pixel budget plus a frame rate, not the word "720p":
|
||
|
||
| | Free | Pro |
|
||
|---|---|---|
|
||
| Pixels | ≤ **921,600** | any shipped tier |
|
||
| Frame rate | ≤ **30 fps** | any shipped tier |
|
||
| Landscape | 1280×720 @30 | 1280×720 @60 · 1920×1080 @30 · @60 |
|
||
| Vertical 9:16 | 720×1280 @30 | 1080×1920 @60 |
|
||
|
||
- **Records too.** The cap governs the file the creator keeps as well as the stream; otherwise
|
||
free 1080p recording is a re-upload hole. One `OutputProfile`, resolved once per session and
|
||
consumed by **both** the stream encoder and the recorder — never two checks.
|
||
- **Fail closed.** Unresolved, unreadable or unvalidated entitlement = free tier. Never Pro.
|
||
- **Never downgrade a running stream.** The gate applies to the next start; pulling quality out
|
||
of a live broadcast is a worse bug than one free 1080p session.
|
||
- **The greyed menu entry is the sales surface.** It stays clickable, says what it costs, opens
|
||
checkout, and is labelled ("720p30 · free tier") — never silently missing, because silence
|
||
reads as broken.
|
||
- **Activation is live**, not restart-gated: activating enables the high tiers and a lapse
|
||
(cancel, revoke, sign-out, channel switch) greys them again on the next refresh.
|
||
- **Bitrate follows the profile** — a capped user is not offered a 1080p bitrate.
|
||
|
||
## Entitlement — two sources, one profile
|
||
|
||
**Pro is `steamDlcOwned || polarKeyValid`.** Both resolve to the same `OutputProfile`; neither
|
||
touches a feature. Fail closed: anything unresolved is the free tier.
|
||
|
||
### Steam (lifetime)
|
||
|
||
- **Steam owns the lifetime SKU.** `ISteamApps::BIsDlcInstalled(dlcAppId)` after `SteamAPI.Init`.
|
||
It is a local check against Steam's own state — no server, no key, no lease.
|
||
- **Steam down or not running** → free profile, with a line explaining why, never a refusal to
|
||
start. A Steam build launched outside Steam is the common case for people who pin shortcuts.
|
||
- **No licence key exists for the Steam SKU**, so nothing to paste, email or lose.
|
||
|
||
### Polar (monthly, sold off-Steam)
|
||
|
||
**Polar is the merchant of record and the only source of truth** for the monthly plan. It takes the
|
||
money, remits VAT/GST, handles refunds and chargebacks, and holds the keys. **There is no server of
|
||
ours in the loop** — no claim page, no callback endpoint, no key database.
|
||
|
||
- **Key** — `LCYT-` prefix; org `c05fb364-…` and the endpoints are in `CREDENTIALS.md`. No
|
||
product is created yet.
|
||
- **The client talks to Polar directly.** The customer-portal endpoints authenticate on the **key
|
||
itself** plus the `organization_id` (a public identifier). The **org access token must never
|
||
ship in the app** — it is only needed to create products, not to activate them.
|
||
- **Bound to the buyer's YouTube channel ID** (`UC…` from `channels.list?mine=true` — the
|
||
signed-in channel, never the `@handle`) through Polar's **`conditions`**, not through a browser
|
||
flow: `activate` is called with `conditions: { "channel_id": "UC…" }`, and every later `validate`
|
||
passes the same object. Polar compares them server-side, so the binding is enforced without us
|
||
hosting anything.
|
||
- **Two activations**, any machine, one channel (`limit_activations: 2` on the benefit). A second
|
||
channel is a second key. Two keys for one channel is never sold — it doubles the support surface
|
||
and invites "give one to a friend".
|
||
- **Runtime rule:** Pro requires `licence.channelId == connected.channelId`. Equal → Pro.
|
||
Anything else → free tier, working normally, with *"This licence belongs to another channel"*
|
||
and a link to the Polar customer portal. Never an error wall, never a blocked stream.
|
||
- **Lease, not a permanent cache:** validate on launch and daily; **7–14 days offline grace**;
|
||
no network past grace = free tier until Polar answers. Never lock a paying user out on a DNS
|
||
failure.
|
||
- **Device management is Polar's** — the customer portal already lists and deactivates
|
||
activations, so we build no UI for it. A subscription key is **auto-revoked when the
|
||
subscription ends**, which is what makes a lapse a real state on the monthly SKU.
|
||
- **Four states, and nothing else:** valid → Pro · expired/revoked → free with a banner ·
|
||
activation limit reached → "manage devices" portal link · mismatch or offline → free, explained.
|
||
- **CLI activation** — `--activate LCYT-…` so silent and fleet installs never meet a dialog.
|
||
Interactive entry in Settings is the secondary path.
|
||
- **Notice cadence** — 7 days out, 1 day out, and on the day, inline in the go-live panel with a
|
||
date and a portal link. Recording gets the banner only. Never modal, never blocking a Start,
|
||
never orange (orange is the dev-only label colour).
|
||
|
||
⛔ **`conditions` is agreement, not cryptography.** A patched client can send someone else's
|
||
channel ID. That is accepted: the free tier is the defence, the licence is a lease, and a
|
||
determined crack buys little. Do not propose a signed token, a hardware fingerprint or a server
|
||
to "fix" it.
|
||
|
||
## Channels — Steam only
|
||
|
||
**Steam is the distributor.** One free app on the store, the paid edition as a Steam **DLC**, and
|
||
no other vector: no direct download, no winget, no Scoop/Chocolatey, no Microsoft Store. The store
|
||
page is the single authoritative source for the download, the stats and the reviews.
|
||
|
||
- **Lifetime Pro = Steam DLC.** Bought on the **store page**; the app only reads ownership
|
||
(`ISteamApps::BIsDlcInstalled` / `UserHasLicenseForApp`). No purchase flow in the app, no Steam
|
||
Wallet code, nothing to reconcile.
|
||
- **The $10/month plan is sold outside Steam** and is **never purchasable inside a Steam build**.
|
||
Steamworks requires in-app purchases to go through the Steam Wallet — so the app *states* that
|
||
monthly plans exist on the site and does not sell them. If the app ever grows a checkout for it,
|
||
that is the policy violation.
|
||
- **Boot shape:** the app ships `steam_api64.dll` beside the exe and calls `SteamAPI.Init`. That
|
||
is a functional dependency, not the rejected "native helper DLL for IP protection" — Steam
|
||
ownership cannot be checked without it. Launched without Steam running, the app must fall back to
|
||
the **free profile** and say so, never refuse to start.
|
||
- **Updates are Steam's.** The depot push is the update; the app keeps no updater and no version
|
||
banner (the old "no self-updater" rule stays, for the same reason: it works differently).
|
||
- **Portable mode is no longer a distributable.** `portable.txt` remains as a feature for anyone who
|
||
copies the installed folder; it is not a delivery mechanism.
|
||
- **Steam Direct** is **$100 per app**, recoupable after $1,000 adjusted gross revenue.
|
||
- **Price parity:** if the lifetime is ever sold off-Steam, it may not be cheaper than the Steam
|
||
price.
|
||
|
||
**Not channels, ever:** direct download, winget/Chocolatey/Scoop, the Microsoft Store, Patreon or
|
||
any subscription platform of our own, paywalled early access, a paid beta, a Discord. Content is
|
||
the funnel; Steam is the checkout.
|
||
|
||
**Support surfaces this creates:** the Steam store page's support contact, Steam **Announcements**
|
||
for known issues and release notes, Steam Discussions left pinned and unread, and one public issue
|
||
repo for bugs that survive the friction of needing an account.
|
||
|
||
## Signing — not needed for the Steam channel
|
||
|
||
- **Steam installs never meet SmartScreen.** Valve's client delivers the files, so there is no
|
||
browser download, no Mark-of-the-Web and no "unknown publisher" wall. The certificate that
|
||
existed only to soften that wall has nothing to do.
|
||
- **Artifact Signing** (renamed from "Trusted Signing") stays on the shelf: Azure Artifact
|
||
Signing, $9.99/month Basic, requires a paid Azure subscription, individual identity validation
|
||
is the slow part. Buy it the day a non-Steam download exists — the zip case, not this one.
|
||
- **Never EV, never a certificate with an HSM.** Microsoft removed the EV SmartScreen instant-bypass
|
||
in March 2024; it buys nothing.
|
||
- **Steam does not sign our exe either** — it just never puts it through the browser gauntlet.
|
||
Windows may still show a UAC prompt for the app; that is elevation, not publisher trust.
|
||
|
||
## Support
|
||
|
||
**Email. Best effort. No SLA. Refunds granted on request, without argument.**
|
||
|
||
- **There is no training service.** "I want people to succeed" is not "I will train them". The app
|
||
teaches itself — one-click go-live, five scenes, nothing to configure. A how-to question means
|
||
the app or its docs fell short; the answer is a fix or a video, not a conversation.
|
||
- **No helpdesk.** No ticketing system, no live chat, no onboarding calls, no hand-holding.
|
||
- **A repeated question is a video, not a ticket** — see `MARCOM.md` → support → content loop. The
|
||
training channel is the support surface.
|
||
- **The issue tracker is a filter, deliberately.** Bugs go to the public releases repo on GitHub,
|
||
which requires an account; Steam Discussions stay pinned and unread; Announcements carry known
|
||
issues and release notes. Steam refunds are Valve's own (2 h / 14 days) and cost us nothing.
|
||
- **Nobody is locked out over a licence.** A billing hiccup drops the app to the free profile with
|
||
a banner and a portal link — never a blocked stream, never a modal.
|
||
|
||
## Founding 200 promo
|
||
|
||
Cash cost **$0**; it is priced in support time and forgone list revenue.
|
||
|
||
- **90 days free** — a 100%-off Polar coupon, `duration = repeating, months = 3`.
|
||
- **Founder price lock** — their $10/month stays $10 permanently.
|
||
- **Credits + in-app Founder badge** (opt-in channel name on the site).
|
||
- **30-day passes** — unique keys carrying the issuer's channel, issued **one per 30 active days,
|
||
max 3 held**, activation limit 1–2, expiring 30 days **after activation**. Earned, never handed
|
||
out: a pass nobody uses teaches nothing.
|
||
- Optional **$250 lifetime** window in the first 90 days — converting a founder beats three
|
||
months of free.
|
||
- Reminders at day 75 and 85; the renewal cliff is where founders churn.
|
||
- Terms, one paragraph: one per person, passes expire 30 days after activation, no cash value,
|
||
revoked for resale or abuse, price lock void if the subscription lapses.
|
||
|
||
## Costs
|
||
|
||
| Item | Cost |
|
||
|---|---|
|
||
| Domain | ~$15/yr |
|
||
| Droplet | $0 — serves the existing site |
|
||
| Steam Direct | **$100 once**, recoupable after $1,000 AGR |
|
||
| Steam revenue share | 30% on the lifetime DLC → $400 nets **$280** |
|
||
| Polar | 5% + 50¢ per sale → $10 nets $9.00 (monthly only) |
|
||
| Steamworks, public issue repo, Gitea | $0 |
|
||
| Azure Artifact Signing — **not needed** | Steam installs don't meet SmartScreen |
|
||
| Microsoft Store — **not used** | — |
|
||
|
||
Fixed year one ≈ **$115** (domain + Steam Direct, one time). The Steam cut is the price of not
|
||
paying for a certificate, a storefront or an update path.
|
||
|
||
**Signing is now moot as a launch cost.** Steam installs never meet SmartScreen, so the certificate
|
||
that existed only to soften that wall has no job. Artifact Signing stays on the shelf for the day a
|
||
non-Steam download exists.
|
||
|
||
## Anti-piracy posture
|
||
|
||
**Any check that runs on the user's machine can be removed.** The goal is a crack that is
|
||
pointless and a leak that is traceable — not an unbreakable binary.
|
||
|
||
- **The free tier is the real defence.** If the only delta is pixels above 921,600, a cracked
|
||
build buys almost nothing and forfeits updates, support and the publisher's signature.
|
||
- **Steam DRM is optional and not needed.** The DLC check is already the entitlement; adding
|
||
Valve's DRM wrapper buys nothing a determined crack respects, and it can break an exe on a
|
||
machine that never runs Steam.
|
||
- **The distribution is not a handout.** Steam deliveries go through Valve's client and account, so
|
||
there is no public zip for a mirror site to repackage, and no hash pin for us to maintain.
|
||
- **Short leases** — a revoked or refunded Polar key dies inside the grace window; a refunded Steam
|
||
DLC stops reporting ownership the next time Steam is asked.
|
||
- **Licence ID in About** — a leaked build traces back to a purchase.
|
||
|
||
⛔ **Rejected, do not re-propose:** a native helper DLL for "IP protection", obfuscation, hardware
|
||
locking, remote code that downloads and executes, and gating asset downloads as an auth check.
|
||
None of them make a licence enforceable; the first three add failure modes without buying
|
||
anything, the fourth needs the server this plan exists to avoid, and the fifth is Store policy
|
||
10.2.2.
|