From 6eab191cb8de21749f858c3964f41106e28bf954 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Fri, 22 May 2026 10:15:06 -0700 Subject: [PATCH 1/2] docs(licensing): align ThreadPlane branding, add /docs/licensing landing page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Editorial cleanup across all licensing-touching surfaces: - New /docs/licensing landing page (linked from email + footer + /thanks) - Email copy: "Thanks for subscribing" → "Thanks for your ThreadPlane license purchase"; CACHEPLANE_LICENSE → THREADPLANE_LICENSE in installation snippet; install snippet now shows provideChat() pattern (the actual API), not process.env-only - Brand: "Threadplane" → "ThreadPlane" in customer-visible copy and library NOTICE/README files - License name: always "ThreadPlane Commercial license" (capitalized) - Tense: drop "Starting with the next published version" from COMMERCIAL.md and root README — the license is live - Eval policy: precise "30 calendar days from first commercial use, good-faith, no telemetry" wording in libs/chat/COMMERCIAL-USE.md and PricingFAQ - Tier scoping: standardize "Indie" name, prefix paid features with "ThreadPlane Commercial license", drop Enterprise "starting at $10k/year" display period (was inconsistent with "Custom") - Footer "Licensing" link → /docs/licensing (was /pricing#faq) - /thanks page button → /docs/licensing (was /docs/chat/...) Co-Authored-By: Claude Opus 4.7 (1M context) --- COMMERCIAL.md | 8 +- README.md | 2 +- apps/minting-service/src/lib/email.spec.ts | 6 +- apps/minting-service/src/lib/email.ts | 34 +- apps/website/src/app/docs/licensing/page.tsx | 302 ++++++++++++++++++ apps/website/src/app/pricing/page.tsx | 6 +- apps/website/src/app/thanks/page.tsx | 6 +- .../src/components/pricing/PricingFAQ.tsx | 4 +- apps/website/src/components/shared/Footer.tsx | 8 +- libs/chat/COMMERCIAL-USE.md | 2 +- libs/chat/NOTICE.md | 4 +- libs/chat/README.md | 18 +- pricing/tiers.config.ts | 12 +- 13 files changed, 360 insertions(+), 52 deletions(-) create mode 100644 apps/website/src/app/docs/licensing/page.tsx diff --git a/COMMERCIAL.md b/COMMERCIAL.md index 963690f8..421a62ff 100644 --- a/COMMERCIAL.md +++ b/COMMERCIAL.md @@ -4,12 +4,10 @@ Most libraries in this repository — `@ngaf/render`, `@ngaf/agent`, `@ngaf/lang ## `@ngaf/chat` -Starting with the next published version, `@ngaf/chat` is dual-licensed: +`@ngaf/chat` is dual-licensed: -- **PolyForm Noncommercial 1.0.0** for free noncommercial use (personal, hobby, student, academic, nonprofit, public demos, OSI-licensed open source, 30-day commercial evaluation). -- **Threadplane commercial license** for commercial production use. - -Historical MIT releases of `@ngaf/chat` remain under their original terms. +- **PolyForm Noncommercial 1.0.0** for free noncommercial use (personal, hobby, student, academic, nonprofit, public demos, OSI-licensed open source, 30 calendar days of commercial evaluation from first commercial use). +- **ThreadPlane Commercial license** for commercial production use. Sold via [threadplane.ai/pricing](https://threadplane.ai/pricing); see [/docs/licensing](https://threadplane.ai/docs/licensing) for installation. See [`libs/chat/LICENSE.md`](./libs/chat/LICENSE.md), [`libs/chat/LICENSE-COMMERCIAL.md`](./libs/chat/LICENSE-COMMERCIAL.md), and [`libs/chat/COMMERCIAL-USE.md`](./libs/chat/COMMERCIAL-USE.md) for the full terms. diff --git a/README.md b/README.md index d818dc90..0e4c078c 100644 --- a/README.md +++ b/README.md @@ -131,4 +131,4 @@ That's it. `chat.messages()` and `chat.status()` are Angular Signals. Bind them Most libraries in this repository (`@ngaf/render`, `@ngaf/agent`, `@ngaf/langgraph`, `@ngaf/ag-ui`, `@ngaf/a2ui`, `@ngaf/licensing`, `@ngaf/telemetry`, `@ngaf/design-tokens`) are released under the **MIT License** — free for any use, including commercial, with attribution. -**`@ngaf/chat`** is the exception. Future versions are licensed under **PolyForm Noncommercial 1.0.0 OR a Threadplane commercial license**. Historical npm releases remain MIT. See [`libs/chat/LICENSE.md`](./libs/chat/LICENSE.md), [`libs/chat/COMMERCIAL-USE.md`](./libs/chat/COMMERCIAL-USE.md), and [`COMMERCIAL.md`](./COMMERCIAL.md) for details. +**`@ngaf/chat`** is the exception. It is dual-licensed under **PolyForm Noncommercial 1.0.0** for free noncommercial use, or a **ThreadPlane Commercial license** for production use inside a for-profit context. See [`libs/chat/LICENSE.md`](./libs/chat/LICENSE.md), [`libs/chat/COMMERCIAL-USE.md`](./libs/chat/COMMERCIAL-USE.md), [`COMMERCIAL.md`](./COMMERCIAL.md), and [threadplane.ai/docs/licensing](https://threadplane.ai/docs/licensing) for details. diff --git a/apps/minting-service/src/lib/email.spec.ts b/apps/minting-service/src/lib/email.spec.ts index 19e575ee..43a81d24 100644 --- a/apps/minting-service/src/lib/email.spec.ts +++ b/apps/minting-service/src/lib/email.spec.ts @@ -10,9 +10,9 @@ describe('renderLicenseEmail', () => { expiresAt: new Date('2027-04-20T00:00:00Z'), }); - expect(out.text).toContain('-----BEGIN CACHEPLANE LICENSE-----'); + expect(out.text).toContain('-----BEGIN THREADPLANE LICENSE-----'); expect(out.text).toContain('PAYLOAD.SIG'); - expect(out.text).toContain('-----END CACHEPLANE LICENSE-----'); + expect(out.text).toContain('-----END THREADPLANE LICENSE-----'); }); it('subject includes tier and seat count with plural s for seats > 1', () => { @@ -54,6 +54,6 @@ describe('renderLicenseEmail', () => { }); expect(out.html).toContain(' + // .env + THREADPLANE_LICENSE= Docs: https://threadplane.ai/docs/licensing Questions: reply to this email. @@ -48,16 +51,21 @@ Questions: reply to this email. -- The ThreadPlane team `; - const html = `

Thanks for subscribing to ThreadPlane.

-

Your license token is below. Set it as the CACHEPLANE_LICENSE environment variable in your application:

-
-----BEGIN CACHEPLANE LICENSE-----
+  const html = `

Thanks for your ThreadPlane license purchase.

+

Your license is valid for 12 months from today. Paste the token below into your @ngaf/chat configuration:

+
-----BEGIN THREADPLANE LICENSE-----
 ${escapeHtml(vars.token)}
------END CACHEPLANE LICENSE-----
+-----END THREADPLANE LICENSE-----

Tier: ${escapeHtml(vars.tier)}
Seats: ${vars.seats}
Expires: ${escapeHtml(expiresIso)}

Installation:

-
export CACHEPLANE_LICENSE="<paste token above>"
+
provideChat({
+  license: process.env['THREADPLANE_LICENSE'],
+});
+
+// .env
+THREADPLANE_LICENSE=<paste token above>

Docs: threadplane.ai/docs/licensing
Questions: reply to this email.

-- The ThreadPlane team

diff --git a/apps/website/src/app/docs/licensing/page.tsx b/apps/website/src/app/docs/licensing/page.tsx new file mode 100644 index 00000000..21430f4d --- /dev/null +++ b/apps/website/src/app/docs/licensing/page.tsx @@ -0,0 +1,302 @@ +import Link from 'next/link'; +import { tokens } from '@ngaf/design-tokens'; +import { Container } from '../../../components/ui/Container'; +import { Section } from '../../../components/ui/Section'; +import { Eyebrow } from '../../../components/ui/Eyebrow'; +import { Button } from '../../../components/ui/Button'; +import { createPageMetadata } from '../../../lib/site-metadata'; + +export const metadata = createPageMetadata({ + title: 'Licensing — ThreadPlane', + description: + 'How the ThreadPlane Commercial license works, who needs one, and how to install your license token in @ngaf/chat.', + pathname: '/docs/licensing', + type: 'website', +}); + +const headingStyle = { + fontFamily: tokens.typography.h2.family, + fontSize: tokens.typography.h2.size, + lineHeight: tokens.typography.h2.line, + fontWeight: 700, + color: tokens.colors.textPrimary, + margin: 0, + marginBottom: 16, + letterSpacing: '-0.01em', +} as const; + +const h3Style = { + fontFamily: tokens.typography.h3.family, + fontSize: tokens.typography.h3.size, + lineHeight: tokens.typography.h3.line, + fontWeight: 600, + color: tokens.colors.textPrimary, + margin: 0, + marginTop: 24, + marginBottom: 8, +} as const; + +const bodyStyle = { + fontFamily: tokens.typography.body.family, + fontSize: tokens.typography.body.size, + lineHeight: tokens.typography.body.line, + color: tokens.colors.textSecondary, + margin: '0 0 16px', + maxWidth: '64ch', +} as const; + +const codeBlockStyle = { + fontFamily: tokens.typography.fontMono, + fontSize: 13, + lineHeight: 1.6, + background: tokens.surfaces.surfaceTinted, + border: `1px solid ${tokens.surfaces.border}`, + borderRadius: 8, + padding: 16, + overflow: 'auto', + color: tokens.colors.textPrimary, + margin: '0 0 16px', + whiteSpace: 'pre' as const, +} as const; + +const tableStyle = { + width: '100%', + borderCollapse: 'collapse' as const, + fontFamily: tokens.typography.body.family, + fontSize: 14, + color: tokens.colors.textSecondary, + margin: '0 0 24px', +} as const; + +const cellStyle = { + padding: '10px 12px', + borderBottom: `1px solid ${tokens.surfaces.border}`, + verticalAlign: 'top' as const, +} as const; + +const headerCellStyle = { + ...cellStyle, + color: tokens.colors.textPrimary, + fontWeight: 600, + background: tokens.surfaces.surfaceTinted, +} as const; + +export default function LicensingPage() { + return ( + <> +
+ +
+ Documentation +

+ Licensing +

+

+ How the ThreadPlane licensing model works, who needs a paid license, and how to install your license token. +

+
+
+
+ +
+ +
+

The model

+

+ Agent UI for Angular is a suite of libraries. Most are{' '} + MIT-licensed and free for any use, + commercial or not. Only @ngaf/chat is + dual-licensed. +

+

+ @ngaf/chat is source-available under{' '} + PolyForm Noncommercial 1.0.0 for free + noncommercial use, or a ThreadPlane Commercial license{' '} + for production use inside a for-profit context. The same source ships under both — you don't get a + different build. +

+ +

Do you need a paid license?

+

+ You need a ThreadPlane Commercial license if you use @ngaf/chat{' '} + in any of: +

+
    +
  • A commercial product or SaaS
  • +
  • An internal business tool inside a for-profit company
  • +
  • An agency deliverable or paid client project
  • +
  • Any application operated by or for a for-profit entity
  • +
+

You do not need a paid license for:

+
    +
  • Personal, hobby, student, academic, or nonprofit projects
  • +
  • Public demos and tutorials
  • +
  • Open-source applications released under an OSI-approved license
  • +
  • Commercial evaluation, up to 30 calendar days from your first commercial use
  • +
+
+
+
+ +
+ +
+

Install your license

+

+ After purchase, ThreadPlane emails a signed license token to the address on your receipt. Paste it + into your app's provideChat(){' '} + configuration: +

+
{`// app.config.ts
+import { ApplicationConfig } from '@angular/core';
+import { provideChat } from '@ngaf/chat';
+
+export const appConfig: ApplicationConfig = {
+  providers: [
+    provideChat({
+      license: process.env['THREADPLANE_LICENSE'],
+    }),
+  ],
+};`}
+

+ The library verifies the token's Ed25519 signature on boot. The check is{' '} + advisory-only: a missing, expired, or + tampered token logs a console.warn but + never blocks rendering. Verification is fully offline; no calls leave your app at runtime. +

+

+ The token is safe to commit to a private repository, or to read from a build-time environment variable + for public repos. Public-repo demos are exempt from the commercial-use definition, but if your + public repo backs a commercial product, the deployed bundle does need a license. +

+
+
+
+ +
+ +
+

Tier scoping

+

+ Pick the tier that matches how you'll deploy. All paid tiers grant the same{' '} + ThreadPlane Commercial license; the difference is the scope of use and the number of seats. +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TierDevelopersCommercial appsBest for
Indie — $149/yr11Solo devs, single indie product
Developer Seat — $299/dev/yrPer seatUnlimited apps owned by your orgStartups, growing teams
App Deployment — $1,499/app/yrUnlimited1 production app per licenseAgencies, CI/CD-heavy teams
Enterprise — customCustomMulti-app, custom termsProcurement, SLA, security review
+
+

+ Every license is valid for 12 months from the date of purchase. Renewal is a fresh annual purchase — + we don't auto-charge. +

+
+
+
+ +
+ +
+

Evaluation

+

+ You may use @ngaf/chat commercially + for 30 calendar days from your first + commercial use as a good-faith evaluation. There is no telemetry, no registration, no email check — + we trust you to count the days. After 30 days you must either purchase a license or stop the + commercial use. +

+ +

Refunds

+

+ If you refund a license through Stripe, the token is revoked automatically and we email a confirmation. + The verification check warns on boot. There's no clawback of the source code you already have — + everything is source-available under PolyForm Noncommercial by default. +

+ +

Questions

+

+ Volume pricing, multi-app licensing, audit clauses, custom terms — any of those, reach out and we'll + work it out. +

+
+ + + + Pricing FAQ → + +
+
+
+
+ + ); +} diff --git a/apps/website/src/app/pricing/page.tsx b/apps/website/src/app/pricing/page.tsx index de07919f..52c3d478 100644 --- a/apps/website/src/app/pricing/page.tsx +++ b/apps/website/src/app/pricing/page.tsx @@ -13,7 +13,7 @@ import { createPageMetadata } from '../../lib/site-metadata'; export const metadata = createPageMetadata({ title: 'Pricing — Agent UI for Angular', description: - '@ngaf/chat is free for noncommercial use under PolyForm Noncommercial 1.0.0. Commercial production use requires a Threadplane license. Other libraries remain MIT.', + '@ngaf/chat is free for noncommercial use under PolyForm Noncommercial 1.0.0. Commercial production use requires a ThreadPlane Commercial license. Other libraries remain MIT.', pathname: '/pricing', type: 'website', }); @@ -67,7 +67,7 @@ export default function PricingPage() { margin: 0, }} > - @ngaf/chat is free for noncommercial use. Commercial production use requires a Threadplane license. Other libraries in the framework remain MIT. + @ngaf/chat is free for noncommercial use. Commercial production use requires a ThreadPlane Commercial license. Other libraries in the framework remain MIT.

@@ -126,7 +126,7 @@ export default function PricingPage() {
- Because commercial use requires a license, @ngaf/chat is source-available rather than OSI open source. Threadplane keeps ecosystem packages (@ngaf/render, @ngaf/agent, @ngaf/langgraph, @ngaf/ag-ui, @ngaf/a2ui, @ngaf/licensing, @ngaf/telemetry, @ngaf/design-tokens) permissively MIT-licensed. + Because commercial use requires a license, @ngaf/chat is source-available rather than OSI open source. ThreadPlane keeps ecosystem packages (@ngaf/render, @ngaf/agent, @ngaf/langgraph, @ngaf/ag-ui, @ngaf/a2ui, @ngaf/licensing, @ngaf/telemetry, @ngaf/design-tokens) permissively MIT-licensed.
diff --git a/apps/website/src/app/thanks/page.tsx b/apps/website/src/app/thanks/page.tsx index 1dd98470..cf2f5cb9 100644 --- a/apps/website/src/app/thanks/page.tsx +++ b/apps/website/src/app/thanks/page.tsx @@ -7,7 +7,7 @@ import { Button } from '../../components/ui/Button'; import { createPageMetadata } from '../../lib/site-metadata'; export const metadata = createPageMetadata({ - title: 'Payment received — Threadplane', + title: 'Payment received — ThreadPlane', description: 'Your @ngaf/chat license token will be emailed shortly.', pathname: '/thanks', type: 'website', @@ -57,8 +57,8 @@ export default function ThanksPage() { If you don't see the email within 10 minutes, check spam or contact us.

- )} - + ); })}
+ +

+ All paid tiers include the ThreadPlane Commercial license · One-time annual payment · 12-month validity +

); diff --git a/pricing/tiers.config.ts b/pricing/tiers.config.ts index 360dfd14..7e7b29d0 100644 --- a/pricing/tiers.config.ts +++ b/pricing/tiers.config.ts @@ -21,8 +21,13 @@ export interface TierConfig { /** USD cents. null for free / custom. */ readonly priceCents: number | null; readonly displayPrice: string; + /** Short suffix rendered inline after the price, e.g. "/dev/yr". */ readonly displayPeriod: string; + /** Subtitle under the price; replaces the standalone period gray subline. */ + readonly subtitle: string; readonly features: readonly string[]; + /** Short one-liner shown in its own row below the features. */ + readonly bestFor: string; /** false → community (npm), enterprise (sales). true → real Stripe product + price. */ readonly stripeBuyable: boolean; /** Highlighted card in the PricingGrid. */ @@ -36,17 +41,17 @@ export interface TierConfig { export const TIERS: readonly TierConfig[] = [ { slug: 'community', - name: 'Community / Noncommercial', + name: 'Community', priceCents: null, displayPrice: 'Free', - displayPeriod: 'forever', + displayPeriod: '', + subtitle: 'forever', features: [ - 'Personal, student, academic, nonprofit, demo', + 'Personal, OSS, demos', 'Source access', - 'Noncommercial use', - 'Commercial evaluation (30 days)', - 'License: PolyForm Noncommercial 1.0.0', + '30-day commercial eval', ], + bestFor: 'Tinkering, OSS projects, students', stripeBuyable: false, highlight: false, }, @@ -55,14 +60,14 @@ export const TIERS: readonly TierConfig[] = [ name: 'Indie', priceCents: 14900, displayPrice: '$149', - displayPeriod: '/year', + displayPeriod: '/yr', + subtitle: 'one app · one dev', features: [ '1 developer', '1 commercial app', 'Unlimited end users', - 'ThreadPlane Commercial license', - 'Best for: solo devs, indie products, consultants with one app', ], + bestFor: 'Solo founders, indie SaaS', stripeBuyable: true, highlight: false, }, @@ -71,14 +76,14 @@ export const TIERS: readonly TierConfig[] = [ name: 'Developer Seat', priceCents: 29900, displayPrice: '$299', - displayPeriod: '/developer/year', + displayPeriod: '/dev/yr', + subtitle: 'per developer', features: [ - 'Unlimited apps owned by your org', - 'Unlimited end users', - 'Dev / staging / production', - 'ThreadPlane Commercial license', - 'Best for: startups & growing teams', + 'Per developer seat', + 'Unlimited apps', + 'Dev · staging · prod', ], + bestFor: 'Startups, in-house teams', stripeBuyable: true, highlight: true, adjustableQuantity: true, @@ -89,14 +94,14 @@ export const TIERS: readonly TierConfig[] = [ name: 'App Deployment', priceCents: 149900, displayPrice: '$1,499', - displayPeriod: '/app/year', + displayPeriod: '/app/yr', + subtitle: 'per production app', features: [ 'Unlimited developers', '1 production app', - 'Unlimited end users', - 'ThreadPlane Commercial license', - 'Best for: agencies, CI/CD-heavy teams', + 'Procurement-friendly', ], + bestFor: 'Agencies, CI/CD-heavy teams', stripeBuyable: true, highlight: false, }, @@ -105,14 +110,14 @@ export const TIERS: readonly TierConfig[] = [ name: 'Enterprise', priceCents: null, displayPrice: 'Custom', - displayPeriod: 'annual', + displayPeriod: '', + subtitle: 'annual contract', features: [ - 'Custom contract & SLA', - 'Procurement support', - 'Security review', 'Multi-app licensing', - 'Priority + private support channel', + 'SLA + security review', + 'Private support channel', ], + bestFor: 'Procurement-led orgs', stripeBuyable: false, highlight: false, },