Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
061a861
Add the pricing spec for Dormouse Hosted
nedtwigg Sep 9, 2026
0d08061
Build the Hosted page on the pricing spec
nedtwigg Sep 10, 2026
4347bcc
Lay the tiers out as a card row
nedtwigg Sep 10, 2026
cb38910
Merge origin/main into pricing-page-fable
nedtwigg Oct 1, 2026
40ce5ce
Sell Hosted as live: drop the coming-soon copy
nedtwigg Oct 1, 2026
398890a
Merge origin/main into pricing-page-fable
nedtwigg Oct 2, 2026
316334d
Lay out Hosted pricing as Free / Hosted / Founding cards
nedtwigg Oct 2, 2026
6b22569
Square the plan cards' buttons to the card radius; one license for th…
nedtwigg Oct 2, 2026
859dcd6
Compress the plan cards: tighter lists, one-line billing note, one fo…
nedtwigg Oct 2, 2026
30ecc40
Tighten the plan cards' copy around the walk-away loop
nedtwigg Oct 2, 2026
28a0df2
Free card: the FSL-1.1-MIT terminal, network only if you choose, link…
nedtwigg Oct 2, 2026
a8689fd
Plan cards per the mockup: Free's licence above the price, three perk…
nedtwigg Oct 2, 2026
87533d7
Plan card copy: easy to fork, Pocket and push linked on both cards, e…
nedtwigg Oct 2, 2026
e2c0fc0
Plan card copy: fork link, platforms, we run the server, optional ava…
nedtwigg Oct 2, 2026
3e96b02
Open /hosted with the plan cards
nedtwigg Oct 2, 2026
720ba7a
Merge remote-tracking branch 'origin/main' into pricing-page-fable
nedtwigg Oct 4, 2026
aa0b0b7
Pricing: the account's subscription is the entitlement; no licence, n…
nedtwigg Oct 4, 2026
57b536e
Merge remote-tracking branch 'origin/main' into pricing-page-fable
nedtwigg Oct 7, 2026
f05d9e0
Pre-launch strip atop Home and the reference pages
nedtwigg Oct 7, 2026
773926b
Sign in, not activate; no per-Pane voice claim yet
nedtwigg Oct 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ A spec is the accurate reference for the current code: it states the invariants
- **`docs/specs/one-time.md`** — One-time connection: the link a laptop shows, its Settings panel and Baseboard indicator, the Hosted rendezvous wire that carries only its handshake, the phone page Hosted serves, and the direct-only session; no account, nothing saved.
- **`docs/specs/security-hosted.md`** — Hosted account origin, identity, and deployment security checks.
- **`SELF_HOST.md`** (repo root) — Self-host deployment: the assistant-run install runbook plus the Installer contract that `docs/specs/security-remote.md`'s `FAIL IF` lines and `scripts/deploy-lint.mjs` audit.
- **`docs/specs/pricing.md`** — Pricing (design-stage): the tiers and founding ladder, what the Individual plan grants, how a desktop proves membership, the managed-voice boundary, and the `/hosted` page contract.
- **`docs/specs/pocket-app.md`** — Pocket: the remote session is a `PlatformAdapter` (`RemotePtyAdapter`), so Pocket is auth screens plus the mobile composition; owns the same-origin deployment rule.
- **`docs/specs/deploy.md`** — Release process: artifact matrix, release checklist, two-stage sign-and-release pipeline, updater manifest, changelog flow.
- **`docs/specs/security.md`** — The guarantees Dormouse makes, what it does not defend, the known gaps, and how it is all checked; published at `/security`, rows split by audience. Read first for anything security. It alone states each known gap and accepted risk; other security specs and the audit preamble point at it. Root `SECURITY.md` is the GitHub policy pointer at it.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ A multitasking terminal for VS Code and the desktop — a real tiling layout, tm
- [Compatible agents](https://dormouse.sh/compatible-agents) — conversation recovery, watching, and contributing an agent integration
- [Agent skill](https://dormouse.sh/agent-skill) — the operating guide Dormouse bundles for coding agents
- [Self-host](https://dormouse.sh/self-host) — run the coordinating Relay on your own tailnet
- [Hosted](https://dormouse.sh/hosted/) — upcoming managed Relay and ElevenLabs voice options
- [Hosted](https://dormouse.sh/hosted/) — managed Relay and ElevenLabs voice, from $10 a month
- [Security](https://dormouse.sh/security) — what Dormouse guarantees, what it does not, and how that is checked

## Features
Expand Down
2 changes: 1 addition & 1 deletion docs/specs/hosted.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,4 @@ Source of truth: `.github/workflows/hosted-production.yml`; `hosted/scripts/prod

1. Deploy the configured providers and pass real production acceptance. pgstencil includes the Microsoft fix; personal and work/school callbacks need acceptance.
2. Add per-browser login listing/revocation, sign-out-everywhere, and account recovery before broad paid use. Revisit the fixed 24-hour login lifetime for daily voice use.
3. Managed voice beyond the admin slice: a real entitlement or licence replacing `ADMIN_EMAIL`, credentials scoped for non-admin accounts, per-account quotas, usage accounting, and spending bounds beyond the fixed daily cap, and explicit text/redaction disclosure.
3. Managed voice beyond the admin slice: the subscription replacing `ADMIN_EMAIL` (`docs/specs/pricing.md` -> "Checkout and entitlement"), credentials scoped for non-admin accounts, per-account quotas, usage accounting, and spending bounds beyond the fixed daily cap, and explicit text/redaction disclosure.
134 changes: 134 additions & 0 deletions docs/specs/pricing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Pricing

> - See `docs/specs/glossary.md` for Burrow / Client / Relay and Pane / Session vocabulary.
> - **Owns:** the plans, the founding ladder, what a plan grants, how a desktop proves membership, the managed-voice boundary, and the content contract of the Hosted page.
> - **Defers:** page chrome, rail, and link obligations to `docs/specs/website-docs.md` -> "Reference page chrome"; the Hosted Relay's accounts, enrollment, and entitlement, and managed voice's routes, to `docs/specs/hosted.md`; the cloud-hosted trust boundary to `docs/specs/security-remote.md` -> "Cloud-hosted mode"; alarm delivery to `docs/specs/alert.md` -> "Spoken alarms".
> - **Status:** the Hosted page publishes the plans and the FAQ; everything that takes money — checkout, the entitlement, managed voice, the hosted Relay — is under [Future](#future).

## The Hosted page

**`/hosted` is canonical, titled "Dormouse Hosted"; `/pricing` 301-redirects to it.** The header nav and the rail label do not change: the tool is free and open source, and Hosted is the optional service with a price, so pricing is a section of the Hosted page, never a page of its own.

**Settings is the front door.** The spoken-alarm row's managed-voice link and the playground tutorial land on `/hosted#voice`, and the plan cards sit within one screen of that anchor. `#remote-control` and `#voice` keep resolving as section ids.

**Content, in order:** the plan cards, directly under the title and anchored `#pricing`; what a member gets, as prose; "Self-hosting stays free"; and a short FAQ — refunds and cancellation, the founding lock, who appears in the founders row, what happens if Hosted shuts down, and that team pricing goes by email to `teams@dormouse.sh`.

**Prices, inclusions, and the FAQ are prerendered text**, and the page emits `Product` / `Offer` JSON-LD carrying one `Offer` per paid plan at its current price, so an assistant fetching the page can quote it. **Offers stay `PreOrder` while checkout is unbuilt.**

**Every price on the site has one owner**: the page, the structured data, and the tests read `website/src/lib/hosted-pricing.ts` rather than restating a number.

**Must describe both grants as live, never upcoming.** The managed Relay section carries the one qualifier: the independent review `docs/specs/security-remote.md` -> "Cloud-hosted mode" requires.

### Plan cards

Three cards — Free, Hosted, Founding — side by side from `md` up, stacked in that order below. Each names what it includes as a ticked list, and each paid card carries a 30-day refund beneath its buy button.

| Card | Above the price | Price line | Includes | Action |
|---|---|---|---|---|
| Free | the terminal's licence | $0, no account, no card | Pocket over a self-hosted Relay, spoken alarms in the system voice, no network request unless a Relay is chosen | Download |
| Hosted | Monthly / Yearly toggle | the toggled price | the managed Relay, managed voices, one account for every machine | Get Hosted |
| Founding | the lock | founding price, list struck beside it | everything in Hosted, the badge and the founders row | Become a founder |

- **Mark the Hosted card with the accent border, never a surface of its own**, which would be a tint no docs token is derived against.
- **The toggle defaults to Monthly**, the prerendered state; switching swaps the price, the billing line, and the buy target in place.
- **A buy button opens the unbuilt-checkout notice** — the plan's name, that nothing was charged and no seat taken, and the devlog. **Never render a buy button that silently does nothing.**

### The founding card's live half

Seats left in the open cohort and the founders row load after hydration from one endpoint; the price beside them is prerendered.

- **Count seats on the server** from the billing provider behind a cache of at most 60 seconds, never on the client and never stored. **Never show a count for a closed cohort.**
- **Show a founder only if they opted in at checkout**; the box starts unticked and the account can untick it. Every other founder counts toward the `+N` that ends the row, as does everyone past the row's cap.
- **Must serve avatars from this origin, never the OAuth provider**, so loading the page tells no provider about the reader. The client draws an initial for any avatar that is not a same-origin path, or that fails to load.
- **The page prerenders without the endpoint**: the seats line is reserved and the row absent; an unreachable endpoint, a non-2xx, or a malformed field drops only that field, never an error. **A cohort closing raises the price at the next deploy.**

**Every existing link keeps working unchanged**: the `linkedFrom` obligations, the root README, `vscode-ext/README.md`, the Settings dialog's voice link, and the hosting notice all already point at `/hosted`. `docs/specs/website-docs.md` -> "Reference page chrome" owns the mechanics.

### Published prices

Prices in USD, and the merchant of record adds or includes tax by jurisdiction.

| Plan | Price | Cadence |
|---|---|---|
| Hosted monthly | $10 | monthly; the reference price |
| Hosted yearly | $100 | yearly; the list founding is read against |
| Founding | $50, rising $10 per closed cohort of 100 | yearly |

- **Never discount the monthly price**; every other price is read against it.
- **Yearly is two months free** against monthly ($100 against $120).
- **The step is $10 per cohort of 100, fixed**, and the ladder's last step is the one below list — reaching list closes founding.
- **Show the current price, the struck list price, and the seats left at that price — never the next step or how many cohorts remain.**

Source of truth: `tiersOnSale`, `foundingTier`, and `pricingJsonLd` in `website/src/lib/hosted-pricing.ts`; `fetchCohort` in `website/src/lib/hosted-cohorts.ts`; `website/src/pages/Hosted.tsx`; the `/pricing` rule in `website/public/_redirects`, pinned by `checkPricingRedirect` in `scripts/public-docs-lint.mjs`. `website/src/pages/Hosted.test.tsx` pins the page contract.

## Future

**Scope: hosted-sales** — what remains, in staged order:

1. **The cohort endpoint** the page already calls: the open cohort's seats and the opted-in founders, avatars proxied onto this origin.
2. **Checkout and entitlement**: purchase, the subscription as the account's entitlement, desktop sign-in, revocation.
3. **Managed voice for members**: the subscription replacing the admin gate (`docs/specs/hosted.md` -> "Managed voice"), the disclosure, one voice per Pane.
4. **Hosted Relay inclusion**: the subscription as the Relay's entitlement (`docs/specs/hosted.md` -> "Relay"), gated on the independent review `docs/specs/security-remote.md` -> "Cloud-hosted mode" requires.
5. **Renewal, cancellation, and refund** paths.

Team and enterprise tiers are never sold through this page. A free hosted tier is undecided — see [Open questions](#open-questions).

### Tiers

What each plan grants once checkout can sell it; [Published prices](#published-prices) is the ladder as the page prints it today.

- **Founding grants the Individual plan plus a founding badge**; monthly and yearly grant the plan alone.
- **A founding lock survives every later price change** and ends only when the subscription lapses; a lapsed founder re-subscribes at list.
- **Cohorts close by count, never by date.** The count is completed purchases at the billing provider; a refund returns the seat to its cohort.
- **When a cohort closes the price rises one step and the counter resets to 100.**
- **Founding closes only when the ladder reaches list.** Founding means bought at launch pricing; the hosted Relay shipping does not close it.
- **Checkout honors the price it opened at.** Concurrent checkouts may oversell a cohort by a few seats; the overage is the customer's, and the next cohort still opens at a full 100.
- **List may rise while founding is open, and never falls.** Monthly and yearly move together the same day, so list is always a price someone can buy at; the ladder keeps climbing $10 per cohort toward the new list; every founder's lock and the struck price they were shown are unchanged.
- **Never reopen the ladder at a lower step** once a cohort has closed.
- **Founding badges are cosmetic**: in-app and on the credits page, never a capability.

### The Individual plan

| Grant | At launch |
|---|---|
| Managed voices for spoken alarms on every machine the member signs in on | live |
| A member default voice, chosen from a curated set | live |
| Dormouse Hosted: the managed Relay, enrollment of the member's Burrows, sealed push, Pocket without a tailnet | live |
| Founding badge | live for founding |

- **The plan never grants team or enterprise capability** — org accounts, SSO, SCIM, BYOT, audit export.
- **The hosted Relay is part of the plan, never a second purchase.** Reserved: the **hosted-sales** scope reads the plan from the Hosted account's subscription ("Checkout and entitlement"), so a member never signs up twice.
- **Nothing shipped free is ever gated**: the terminal, `dor`, browser panes, the notepad, alerts with the system voice, the self-host Relay, and Pocket over a self-hosted Relay stay free, with no login.

### Checkout and entitlement

- **Stripe Managed Payments runs checkout, subscriptions, and the customer portal as merchant of record, through `@pgstencil/stripe`**, so tax is Stripe's. Dormouse never stores card data. A founding lock is a per-cohort Price; cohort counts come from the billing provider's completed subscriptions.
- **Checkout starts from a Hosted account**: a buy button lands on the account origin, which asks for sign-in first, so the subscription belongs to an account from its first event.
- **Founding checkout offers the founders-row opt-in, unticked**; the account can withdraw it at any time.
- **The success page asks the four Van Westendorp questions**, optional and unsent until answered: too expensive to consider, too cheap to trust, expensive but would consider, a bargain. Their answers inform later list changes.
- **The entitlement is the account's subscription, read on the server on every voice and Relay request.** No licence, no offline verification, and no grace past what the subscription grants; a lapsed member's voices fall back to the system voice and its Burrows to `not-entitled`.
- **A desktop signs in from Settings by device code**, the flow Burrow enrollment already runs (`docs/specs/hosted.md` -> "Burrow enrollment"). The approval mints a desktop credential the host keeps and never hands a webview. Sign-in is the only account surface in the free client.
- **One account covers every machine the member uses.** No device count, no seat count, no activation limit.
- **A refund or chargeback ends the subscription**, so the next request is refused, and the seat returns to its cohort.

### Managed voice

- **Dormouse operates the endpoint and holds the vendor key** (ElevenLabs). A request carries the desktop credential, a voice id, and the text; the response is audio.
- **What leaves the machine is exactly the sanitized spoken label and the voice id** — the `toSpokenText` output in `lib/src/lib/alert-speech.ts`, never terminal content, never a notification body, never a Session id. **Disclose this in the enable flow before the first request**, honoring the promise the Hosted page makes.
- **Cache clips by voice and text on the client** and regenerate only when the label changes; a cache hit makes no request. **Fair use is a daily request cap per member**; past it, the system voice speaks.
- **The system voice is the fallback**, for offline, unentitled, endpoint error, or cap: same delivery rules, same cut-off on attend, never silence because the service failed. Delivery identity, queueing, and cut-off stay owned by `docs/specs/alert.md` -> "Spoken alarms".
- **One voice per Pane.** The member default applies everywhere; a per-Pane override is persisted with the pane's settings and follows the Session through minimize and restore. Doors and headers show nothing new.
- **Pocket speaks only in the foreground** — a web app cannot voice a background push — so the desktop is the primary voice sink. A native Pocket is out of scope here.

### Renewal, cancellation, refund

- **Every plan auto-renews; cancel any time; access runs to period end.**
- **30-day refund on every plan.** A refund revokes.
- **A failed founding renewal gets 30 days of grace before the lock is lost.**
- **A subscription is personal and non-transferable.**
- **No trial**: the 30-day refund is the trial.

### Open questions

- A free hosted tier, no card. It is the only way a stock binary can try Pocket, since the shipped bundle reaches only `*.dormouse.sh` (`docs/specs/relay.md` -> "Relay origin").
- The curated voice set and whether members may bring their own ElevenLabs voice id.
14 changes: 8 additions & 6 deletions docs/specs/website-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ Content invariants, checked by review unless a check is named:
- Pocket is described only as shipped or explicitly in development.
- Browser Surfaces are explained to match [dor-browser.md](dor-browser.md) without exposing persisted params, controller registries, proxy plumbing, or future renderers.
- VS Code command names in getting started exist in `vscode-ext/package.json` (`checkVsCodeCommands`).
- Detailed CLI behavior links to `/dor`; the complete agent operating guide links to `/agent-skill`; the hosted-services preview links to `/hosted` (`checkRoutesToReferences`).
- The guide carries no copied internal future design.
- Detailed CLI behavior links to `/dor`; the complete agent operating guide links to `/agent-skill`; the Hosted service page links to `/hosted` (`checkRoutesToReferences`).
- The guide renders no `TODO:` placeholders and no copied internal future design.

### Marketplace and Open VSX constraints

Expand Down Expand Up @@ -120,15 +120,17 @@ These pages follow the reader's theme, through `DocsLayout`; the rest of the sit

Source of truth: `DOCS_PAGES` in `website/src/lib/docs-pages.ts`; `DocsLayout` in `website/src/components/DocsLayout.tsx`; `website/public/_redirects`.

## `/hosted` preview
## `/hosted`

`docs/specs/pricing.md` -> "The Hosted page" owns what it says and sells; this section owns its place on the site.

**Must describe the account service and link its account app, privacy policy, and terms.**

**Must mark both services — Hosted's Pocket Relay and optional managed voice — unavailable;** terminals stay on an awake, online computer; browser speech and self-hosting remain.
**Must describe the managed Relay as Pocket's Relay:** terminals stay on an awake, online computer, and self-hosting remains. `NotifySignupForm` exposes the `nedshed.dev` devlog handoff and keeps email per tab. **Must use native required-email validation.** `website/src/components/NotifySignupForm.test.tsx` pins all three.

**Must open both hosting pages with the Relay boundary:** Dormouse needs none; a new install opens no connection on its own (`docs/specs/remote-network.md` -> "Policy"); push and a paired phone need a Relay, and a one-time connection only Hosted's rendezvous of its handshake. `/self-host` links `/hosted`; `/hosted` labels hosting pending review, discloses metadata, and links the trust model. `website/src/lib/docs-rail.test.tsx` pins this.
**Must open `/self-host` with the Relay boundary:** Dormouse needs none; a new install opens no connection on its own (`docs/specs/remote-network.md` -> "Policy"); push and a paired phone need a Relay, and a one-time connection only Hosted's rendezvous of its handshake; it links `/hosted`. **`/hosted` opens with its plan cards** instead, and its managed Relay section discloses metadata, labels the review pending, and links the model. `website/src/lib/docs-rail.test.tsx` pins both.

**Must also link the preview from** Pocket marketing/tutorial, self-host docs, and the speech and remote-control settings; `linkedFrom` owns the rest.
**Must also link it from** Pocket marketing/tutorial, self-host docs, and the speech and remote-control settings; `linkedFrom` owns the rest. `/pricing` 301-redirects here rather than becoming a page, pinned by `checkPricingRedirect` in `scripts/public-docs-lint.mjs`.

Source of truth: `Hosted` in `website/src/pages/Hosted.tsx`; `HostingRequirementNotice` in `website/src/components/HostingRequirementNotice.tsx`.

Expand Down
Loading
Loading