165 lines
6.8 KiB
JavaScript
165 lines
6.8 KiB
JavaScript
|
|
// What Silent Mode actually sells, stated honestly.
|
||
|
|
//
|
||
|
|
// The register page shows four hosting options, but they are not four products.
|
||
|
|
// Three of them cost us nothing and are already free with any name: an `h`
|
||
|
|
// record is a transaction, an `ip`/`u` record is a pointer at the customer's own
|
||
|
|
// box, and a customer-run Sia is their renterd, not ours. Only **managed Sia**
|
||
|
|
// is hosting-as-a-service.
|
||
|
|
//
|
||
|
|
// This file keeps that distinction in the data rather than in the copy, so the
|
||
|
|
// UI cannot drift into implying we run a service we do not. Every plan declares:
|
||
|
|
//
|
||
|
|
// kind 'free' | 'managed' | 'referral'
|
||
|
|
// weStore do WE hold the customer's bytes? (the liability question)
|
||
|
|
// recurring does it lapse?
|
||
|
|
// onLapse what the buyer is left with when it does
|
||
|
|
//
|
||
|
|
// THE LAPSE STORY MATTERS MORE THAN THE PRICE. The strongest claim on the page
|
||
|
|
// is that a name needs no renewal, ever. Hosting does lapse. If the two blur
|
||
|
|
// together the flagship claim gets damaged by association, so every recurring
|
||
|
|
// plan must state plainly that the NAME is unaffected — it simply points nowhere
|
||
|
|
// until repointed, and repointing is free and needs nobody's permission.
|
||
|
|
|
||
|
|
export const PLAN_KIND = {
|
||
|
|
free: "free", // included with a name; costs us nothing ongoing
|
||
|
|
managed: "managed", // we store and serve — the only real service
|
||
|
|
referral: "referral", // someone else's product; we just link
|
||
|
|
};
|
||
|
|
|
||
|
|
/** Managed hosting is sold in prepaid periods. See ROADMAP note at the bottom. */
|
||
|
|
export const PERIOD = { year: "1y", quarter: "3m" };
|
||
|
|
|
||
|
|
export const HOSTING_PLANS = [
|
||
|
|
{
|
||
|
|
id: "onchain",
|
||
|
|
kind: PLAN_KIND.free,
|
||
|
|
title: "On the blockchain",
|
||
|
|
record: "h",
|
||
|
|
weStore: false,
|
||
|
|
recurring: false,
|
||
|
|
priceSats: 0n,
|
||
|
|
blurb:
|
||
|
|
"A small site stored inside the transaction itself. No server, no host, " +
|
||
|
|
"permanent as the chain.",
|
||
|
|
limits: "About 150 bytes of HTML — a page, a profile, a link hub.",
|
||
|
|
onLapse: null, // nothing to lapse
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: "own-server",
|
||
|
|
kind: PLAN_KIND.free,
|
||
|
|
title: "Your own server",
|
||
|
|
record: "ip",
|
||
|
|
weStore: false,
|
||
|
|
recurring: false,
|
||
|
|
priceSats: 0n,
|
||
|
|
blurb:
|
||
|
|
"Point the name straight at a machine you run. Works with anything you " +
|
||
|
|
"already host.",
|
||
|
|
limits: "You run the server; we never see the traffic.",
|
||
|
|
onLapse: null,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: "byo-sia",
|
||
|
|
kind: PLAN_KIND.free,
|
||
|
|
title: "Your own Sia",
|
||
|
|
record: "s3",
|
||
|
|
weStore: false,
|
||
|
|
recurring: false,
|
||
|
|
priceSats: 0n,
|
||
|
|
blurb:
|
||
|
|
"Already running renterd, or paying a Sia provider? We set the record and " +
|
||
|
|
"step out of the way.",
|
||
|
|
limits: "You hold the storage contracts. Nothing of yours touches our infrastructure.",
|
||
|
|
onLapse: null,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: "sia-managed-1y",
|
||
|
|
kind: PLAN_KIND.managed,
|
||
|
|
title: "Managed Sia hosting",
|
||
|
|
record: "s3",
|
||
|
|
weStore: true,
|
||
|
|
recurring: true,
|
||
|
|
period: PERIOD.year,
|
||
|
|
// Chipnet placeholder. Sia storage itself is pennies for a website; the cost
|
||
|
|
// here is operational, so this is priced as a flat period fee rather than
|
||
|
|
// per-gigabyte — pretending otherwise would be theatre.
|
||
|
|
priceSats: 250_000n,
|
||
|
|
quotaMB: 500,
|
||
|
|
blurb:
|
||
|
|
"Upload your files and we store them on the Sia network and serve them " +
|
||
|
|
"through the gateway. Encrypted and spread across independent hosts.",
|
||
|
|
limits: "500 MB, renewed yearly.",
|
||
|
|
onLapse:
|
||
|
|
"Your name is unaffected — it is yours permanently either way. If hosting " +
|
||
|
|
"lapses the site stops being served and the name simply points nowhere " +
|
||
|
|
"until you repoint it, which is free and needs nobody's permission.",
|
||
|
|
},
|
||
|
|
];
|
||
|
|
|
||
|
|
/** Third parties. Listed for convenience, sold by us in no sense whatsoever. */
|
||
|
|
export const VPS_REFERRALS = [
|
||
|
|
{ id: "mynymbox", title: "MyNymBox", blurb: "No-KYC VPS and hosting. Monero, Bitcoin, Lightning.", price: "from ~€3.50/mo", url: "https://mynymbox.io" },
|
||
|
|
{ id: "njalla", title: "Njalla", blurb: "Privacy-by-proxy domains and VPS. Crypto accepted.", price: "domains ~€15/yr", url: "https://njal.la" },
|
||
|
|
{ id: "1984", title: "1984 Hosting", blurb: "Iceland-based, privacy-respecting hosting.", price: "varies", url: "https://1984.hosting" },
|
||
|
|
];
|
||
|
|
|
||
|
|
export const planById = (id) => HOSTING_PLANS.find((p) => p.id === id) ?? null;
|
||
|
|
|
||
|
|
/** Plans a buyer actually pays us for. */
|
||
|
|
export const paidPlans = () => HOSTING_PLANS.filter((p) => p.priceSats > 0n);
|
||
|
|
|
||
|
|
/** Everything included free with a name — most of the list, and worth saying so. */
|
||
|
|
export const freePlans = () => HOSTING_PLANS.filter((p) => p.kind === PLAN_KIND.free);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Price a name, optionally with hosting, as ONE payment.
|
||
|
|
*
|
||
|
|
* A buyer purchasing both should pay once, to one address, not twice — the order
|
||
|
|
* system already carries a single deposit address per order, so a bundle is a
|
||
|
|
* line-item list rather than two orders to reconcile.
|
||
|
|
*
|
||
|
|
* `namePriceSats` comes from `quotePrice()` in orders.js, which already includes
|
||
|
|
* the chain costs of minting.
|
||
|
|
*/
|
||
|
|
export function quoteBundle({ namePriceSats, planId = null }) {
|
||
|
|
const lines = [{ id: "name", label: "Name certificate", sats: BigInt(namePriceSats) }];
|
||
|
|
const plan = planId ? planById(planId) : null;
|
||
|
|
if (planId && !plan) throw new Error(`unknown hosting plan "${planId}"`);
|
||
|
|
if (plan && plan.priceSats > 0n) {
|
||
|
|
lines.push({ id: plan.id, label: `${plan.title} (${plan.period ?? "one-off"})`, sats: plan.priceSats });
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
lines,
|
||
|
|
plan,
|
||
|
|
totalSats: lines.reduce((s, l) => s + l.sats, 0n),
|
||
|
|
// True when the buyer is trusting us with bytes, not just with minting.
|
||
|
|
createsHostingObligation: !!plan?.weStore,
|
||
|
|
recurring: !!plan?.recurring,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ROADMAP — covenant subscriptions replace prepaid periods.
|
||
|
|
*
|
||
|
|
* Prepaid periods exist because they work today on the escrow that already
|
||
|
|
* ships. They are not the end state: BCH covenants can express a real
|
||
|
|
* subscription, which BTC cannot. A CashScript contract holds the buyer's funds
|
||
|
|
* and permits the host to withdraw `priceSats` once per period, enforced by the
|
||
|
|
* covenant re-creating itself under a relative timelock.
|
||
|
|
*
|
||
|
|
* That is strictly better than a card subscription in three ways a buyer can
|
||
|
|
* check for themselves: the balance stays theirs, the host structurally cannot
|
||
|
|
* take more than one period's fee per period, and cancellation is unilateral —
|
||
|
|
* they reclaim the remainder without asking anyone.
|
||
|
|
*
|
||
|
|
* It is the SAME primitive as the delivery-escrow covenant in
|
||
|
|
* `Decentralized.DNS/DESIGN-trustless-purchase.md` §4 — a contract that releases
|
||
|
|
* funds only on a condition. Design them together; building them separately
|
||
|
|
* would be doing the hard part twice.
|
||
|
|
*/
|
||
|
|
export const SUBSCRIPTION_ROADMAP = {
|
||
|
|
today: "prepaid periods on the existing escrow",
|
||
|
|
next: "CashScript covenant subscription (pull-per-period, cancel-anytime)",
|
||
|
|
sharesDesignWith: "Decentralized.DNS/DESIGN-trustless-purchase.md §4 (delivery escrow)",
|
||
|
|
};
|