argus/src/lib/plans.js
Local Dev a45320db7d Initial commit — Silent Mode baseline (2026-07-29)
Snapshot of the decentralized-web stack at the point of the resolver+Theseus
rebuild deploy. Includes:

- Argus (BNS engine + resolver daemon + Sia gateway)
- AriadneResolver (Windows Inno installer bundle + Android APK sources +
  Firefox extension)
- TheseusNavigator (Electron browser)
- site/ (silentmode.st content, deployed to Sia at bns/silentmode/)
- design docs, roadmap, protocol spec

Secrets excluded via .gitignore: Argus/sia-s3.json, Argus/wallets.json,
Argus/ca/*.key,*.crt. Build outputs, node_modules, and bundled runtimes
also excluded.

Shipped hashes on dl.silentmode.st at this commit:
  AriadneResolver-Setup-0.1.0.exe    5bcb216eef31ea28ed767e4134ab74bd5ac69dfbd365fd249e9e6938e55c986a
  TheseusNavigator-Setup-0.0.1.exe   7c735e88bad2da3347145adba3016c8f626a18b8422289c8c6ba471972e2952b
  TheseusNavigator-0.0.1-portable.exe 008fd84445babeabb401b2bca40ea9466b24b0e6d6c85104da7640c5c5c84521
  ariadne-v0.2.apk                   635c8f04d44ef855a8390b9eeddb8cd2d50622e81cc4e5004e8daffc1bb0425c
2026-07-29 13:54:34 +02:00

164 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)",
};