Skip to content

Hosted: an upsell for an account with no plan, and the Hosted Relay by name - #1366

Merged
nedtwigg merged 11 commits into
mainfrom
hosted-no-plan-approval
Oct 27, 2026
Merged

nedtwigg merged 11 commits into
mainfrom
hosted-no-plan-approval

Conversation

@nedtwigg

@nedtwigg nedtwigg commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

Revived from the stale #1003 walkthrough findings and rebuilt on current main, then grown into an upsell. It also renames the "managed Relay" to the Hosted Relay across marketing, Settings, and the Terms and Privacy pages.

No plan: an offer instead of a dead end

Entitlement is ADMIN_EMAIL-only today, so almost every account that followed an enrollment link from Dormouse typed the two digits and then got the raw "this account is not entitled to the Hosted Relay" with a Retry connection that could never help. This applied to both purposes, remote control and Natural Voice.

  • Detection
    • Up front: the enroll page reads the entitlement from GET /api/voice (hostedVoice.entitled === false), the one predicate both purposes share. A failed read leaves it null and never counts as no plan.
    • On approval: approveEnrollment maps only a 403 carrying NOT_ENTITLED_ERROR to NoPlanError; any other refusal is still a page error.
  • The offer, in place of the digits. The intro says the account has no Hosted plan yet. An accent card follows:
    • Heading: "Natural Voice comes with Dormouse Hosted" or "Remote control comes with Dormouse Hosted", whichever Dormouse asked for.
    • Benefits: ticked lines led by the requested one.
    • Price: the founding price struck against list, with "Or $10/month". Prices are read from website/src/lib/hosted-pricing.ts, the one owner of every price.
    • See Hosted plans: opens dormouse.sh/hosted/#pricing in a new tab, because the code lives only in this page's memory.
  • Coming back after buying. Returning to the tab re-reads the account, and a refusal from the approve route clears once that read finds a plan, so the digits come back.
  • Remote control only: "Rather run your own Relay for free? See how to self-host" appears after the intro and again under the card. Natural Voice has no self-hosted equivalent.
  • Use another account: signs out and keeps the code. Don't approve stays. No plan wins over a stale login, since signing in again as the same account can't help.
  • Spec: hosted.md → "Burrow enrollment" gains the rule. Its word budget is ratcheted from 4600 to 4650.

Empty Computers list

"No computers yet" now names the real desktop path: Settings → Network → Phones → Hosted Relay → Enroll remote control.

The Hosted Relay

The product is Dormouse Hosted, and the account page, the server's refusal and the specs already called its Relay the Hosted Relay; only the marketing copy said "managed". It now pairs with the "self-hosted Relay" the same pages offer.

  • Copy: renamed on the Hosted page (heading, contents, meta), the JSON-LD, both READMEs, the Pocket playground, and glossary.md, pricing.md and website-docs.md. The #remote-control anchor is unchanged.
  • Terms and Privacy are revised for the rename, so TERMS_VERSION and both pages' "Last updated" move to 2026-10-27, as the Natural Voice rename did. The next sign-in records acceptance of the new version.
  • Settings: in a Hosted build, the choice beside One-time connection under Network → Phones is now Hosted Relay. A self-host build keeps Persistent Relay (relay.md, the Hosted remote-control walkthrough).

Storybook

Hosted stories now scroll. lib/src/index.css hides the body's overflow for the app, and preview-head.html gave it back only to docs pages, so the bottom of every Hosted story was unreachable. All 69 Hosted snapshots are byte-identical.

Test plan

  • hosted/src vitest 17/17. The no-plan tests cover both purposes:

    • the plans link opens a new tab
    • the self-host links show for remote control only
    • the 403 maps to no plan
    • a failed read never shows no plan
    • Use another account keeps the code
    • the digits come back after a plan is bought

    Each test was mutation-checked red.

  • RemoteControlSection tests pin Hosted Relay for a stock build and Persistent Relay for a self-host build; each goes red when the label is hardcoded to the other.

  • hosted policy test (TERMS_VERSION matches the pages' date); website vitest 364

  • Story plays: Enrollment 21/21, and RemoteControlSection, SettingsDialog and NetworkSettings 106/106

  • tsc for lib, standalone and hosted

  • spec, md and public-docs lints

🤖 Generated with Claude Code

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Deploying mouseterm with  Cloudflare Pages  Cloudflare Pages

Latest commit: e244345
Status: ✅  Deploy successful!
Preview URL: https://cc8ea3ae.mouseterm.pages.dev
Branch Preview URL: https://hosted-no-plan-approval.mouseterm.pages.dev

View logs

Base automatically changed from pocket-hosted-prompt to main October 27, 2026 18:18
nedtwigg and others added 11 commits October 27, 2026 11:33
…le empty Computers copy

An account without the entitlement followed an enrollment link, typed the
two digits, and got the raw NOT_ENTITLED_ERROR with a Retry that could never
help. The enroll page now reads the entitlement up front from GET /api/voice
(a failed read never counts as unentitled), maps the approve route's 403
NOT_ENTITLED_ERROR to NoPlanError, and in either case shows why in place of
the digits, with Use another account (sign out, code kept) beside Don't
approve.

The empty Computers list now names the desktop path to enroll one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The no-plan enroll state was a paragraph and a sign-out button. It now
shows an offer card leading with what Dormouse asked for (Natural Voice or
remote control), what any plan includes, the founding price against list
from website/src/lib/hosted-pricing.ts, and See Hosted plans in a new tab,
since the code lives only in this page's memory. Use another account stays
as the secondary path.

Coming back to the tab re-reads the account, and a refusal from the approve
route now clears once that read finds a plan, so a plan bought in the other
tab offers the digits again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lib/src/index.css hides the body's overflow for the app, and Storybook's
preview inherits it; preview-head.html gave scrolling back only to docs
pages. HostedFrame grows its iframe to the page it shows, past the viewport,
so the bottom of a Hosted story was unreachable. Pages holding a Hosted
frame now scroll too. Every Hosted snapshot is byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Natural Voice has no self-hosted equivalent, so its no-plan state does not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The product is Dormouse Hosted, and the account page, the server's refusal
and the specs already called its Relay the Hosted Relay; only the marketing
copy said "managed". The Hosted page's heading, contents and meta, the
JSON-LD, both READMEs, the Pocket playground, the no-plan offer, and the
Terms and Privacy pages now say Hosted Relay, the pair to the "self-hosted
Relay" the same pages offer.

Terms and Privacy are revised for it, so TERMS_VERSION and both pages'
"Last updated" move to 2026-10-27, as the Natural Voice rename did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under Network → Phones, the choice beside One-time connection was
"Persistent Relay" in every build. A Hosted build now labels it Hosted
Relay, the product's name for what it enrolls with; a self-host build keeps
Persistent Relay. The account page's empty Computers copy follows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nedtwigg nedtwigg changed the title Hosted: tell an account with no plan before it types digits; actionable empty Computers copy Hosted: an upsell for an account with no plan, and the Hosted Relay by name Oct 27, 2026
@nedtwigg
nedtwigg force-pushed the hosted-no-plan-approval branch from 3d9fd91 to e244345 Compare October 27, 2026 22:28
@nedtwigg
nedtwigg merged commit 419e276 into main Oct 27, 2026
18 checks passed
@nedtwigg
nedtwigg deleted the hosted-no-plan-approval branch October 27, 2026 23:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants