Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/monetize

docs/v2/idiot-guide.md

monetize — Idiot Guide, version 2

Class: idiot. No jargon, nothing assumed, every step spelled out. This is the guide for the person who runs a ONE box: get a box, feed it, add a site, try a buy, sell, upgrade, terminate. If you know what a guest, a feed and an estate are, read the manual instead. Everything here is as the code on monetize main c9b473a5 and the kit do it on 2026-11-04.

1. The one thing to understand first

monetize is one machine that sells and runs other machines. You rent one small server at a cloud (UpCloud), install monetize on it, and from then on that machine, the ONE box, does the selling for every product you have: it shows the prices, takes the money through Stripe, rents the customer's own servers at the cloud, installs the product on them, keeps them paired to itself, upgrades them, and reminds the customer when the six months are up.

Three words you will meet on every page:

Two things never happen: the ONE box never logs into a customer's machine (they call it), and the ONE box never holds a secret in a plugin (the keys stay on the host).

2. Get a box

What you need on your own machine (a laptop is fine):

Then, in the kit's directory, in this order (--box names the box in solo.toml; take test2 first, it is the test box; every verb prints a plan and changes nothing without --apply):

cargo xtask pins --strict                                  # every plugin matches its sha256 pin
cargo xtask media --where upcloud --box test2 --kind dev --apply     # build and upload the install media of a product
cargo xtask release --where upcloud --box test2 --sign-with ~/keys --apply --publish   # sign the plugin list, put it in the bucket
cargo xtask install --where upcloud --box test2 --apply    # rent the VM, boot the installer, feed it, record it

install stops once and prints the DNS line you must set by hand at your DNS provider (an A record for the box's name); it waits up to two hours for it, then goes on. When it ends, the box is up, fed, and recorded.toml holds its public key. It refuses to start at all without a backup rule and an off-box copy declared in solo.toml (the box's ledger is money).

What you got: one small server (2xCPU-4GB) running monetize, reachable at https://<box>; a door on port 8475 that only your kit's certificate opens; a console behind the box's name.

3. Feed it

The feed is everything the box needs to sell, in one go: the plugins, their prices, the secrets (by name; the values come from your vault files), the Stripe accounts, the cloud, the DNS zone, the mail relay. You write it as solo.toml in the kit; you never edit a file on the box.

cargo xtask feed --where upcloud --box test2            # shows the key NAMES it would send
cargo xtask feed --where upcloud --box test2 --apply    # sends it: PUT /feed on the door

The box judges the whole feed with its own loader before it takes it: a feed that would not start (a product with a price but no Stripe account, a plugin with no signature) is refused as a whole, and the box keeps serving what it had. A feed that passes is live within a few seconds; nothing restarts except when a price changed.

Run feed --apply again whenever you change a price, a product, a secret or a site.

Then point Stripe at the box: cargo xtask hosts --where upcloud --box test2 prints the webhook URL per Stripe account (https://<box>/callback/stripe/<account>); paste each into the Stripe dashboard.

4. Open the console

Open https://<box>/ in a browser. The first time, go to Passkeys, type the admin token the install made (cargo xtask printed where it is; it is never printed itself) into Admin token, give your key a label and press Enrol this key. You are now the operator; there are no roles. Everyone you invite from the same page (Invite an operator, a link that works for an hour, a day or a week) can do everything you can.

Every button that changes something asks for a fresh touch of your key (a "step-up"). Every money button must be pressed twice: the first press arms it and asks for a word of reason, the second press confirms, Cancel disarms.

The pages, left to right: Overview (every estate, one line each, revenue of the last 30 days), Accounts, Guardian (repairs), Test buy, Sites, Iron (what you own at the cloud). Picking an estate opens its own pages: Map, Customers, Money, Offer, Events, Settings.

5. Add a site

A site is declared in solo.toml and fed, then ordered:

[boxes.test2.estates.test2-gunnar-rs]
product = "gunnar"
vendor = "stripe/gunnar"
owner = "you"
operator_site = true        # your own: the EUR 0.50 operator price
test_site = true            # may sell from DEV media
site = "test2.gunnar.rs"
cargo xtask feed --where upcloud --box test2 --apply
cargo xtask site order test2-gunnar-rs --where upcloud --box test2 --size mini --admin you@example.com

Or in the console: Sites → New site → fill in Name, Domain, Product, First admin, tick One of my own sites → Add site; then on the site's row choose Size, type the First admin's e-mail, press Order and pay the EUR 0.50 at the Stripe page the row links as Pay 0.50 EUR. (Your own site costs 50 cents and is booked as a test, never as revenue; the ledger must see a real payment.)

Watch the row's state: ordered → held → laying → installing → paired → ready. The box rents three servers, writes the site's DNS records (test2.gunnar.rs, www., git.) through its one DNS token, installs the product over its own link, and waits until the public front answers. Takes a few minutes. The ready mail goes to the first admin with the sign-in link. Logs on the row shows the install as it runs; a stuck install is explained after 10 minutes and alerted after 20.

If the site's domain is not under your DNS zone, the row says so and the box writes the records it can; set the rest by hand (the alert names them).

6. Try a buy

Before a customer does, you do. Two ways:

  1. Test buy in the console. One button per product and site: hosted-10gb · test2.gunnar.rs or private-small · test2.gunnar.rs. On a Stripe test key the box pays with Stripe's test card by itself; a real account (and, for private-small, real machines) is made, the mail goes to the test mailbox. On a live key the button says "LIVE — you will be charged" and you pay at Stripe's page as a customer would. The Runs table shows where each run stands.
  2. From the kit, without the console: cargo xtask site test-order <site> --size mini --admin you@example.com --apply — the same, refused on a live key.

Then be a customer: open https://test2.gunnar.rs/signup?plan=private, type an account name and your e-mail (once: this is the only place an e-mail is ever typed), press Pay (the button says the exact amount, VAT added at checkout), pay at Stripe, and watch the page: "Payment received", "Being prepared", "Your machines are being set up", "Gunnar is being installed", "Ready". You are sent to Stripe exactly once; coming back shows no second payment. The welcome mail carries the sign-in link.

Clean up: on Sites or the estate's Guardian page, type the name back under "Terminate this estate" and press Terminate. Machines, disks and DNS names go. Nothing is refunded, and no backup is taken.

7. Sell

Selling is what the site does once it is ready; there is nothing to switch on. What a customer can buy, and what you read on the console:

they buy what happens where you see it
an account on the site (free: a 0 kr card check; or a plan with a byte cap) the product makes the account; the welcome mail carries the passkey invite Accounts; the estate's Customers
a private estate paid once at Stripe, six months ahead; the box lays and pairs it; the ready mail Overview (a new row), Accounts (its row with Setup log)
more room, more users, a bigger step paid at Stripe; the box tells the estate to grow its disks the tenant's page under Customers; Money
a renewal (offered from 30 days before the end) six more months at today's price, from the end of the paid period Money

The price of an estate is the cloud's own price for the machines plus 20 %, rounded so that at least 10 % is left after Stripe's fee, six months prepaid; the button says the total. Per site you may set the term and the margin (Sites → Edit → Term months, Margin basis points).

What needs you, and only you: the Money page's "Needs you" (a payment that did not finish: Open checkout or Abandon…), "Owed back" (paid, but no account could be made: Refund… or Refunded by hand…), and the red banner on every page (a step that must not fail failed; do what its "Next:" says, press Ack). Every such case is also a mail to the address you fed as alert_to; the rows of chapter 9 of the manual say what each means.

When a customer stops paying: the estate keeps running 14 days after its paid date (reminders went out 30, 14 and 3 days before), then every machine is stopped with the disks kept, and 60 days after the paid date the estate is deleted. The customer reads every one of those dates in the mails. A hosted account goes read-only at once and is deleted after 10 days (mails on day 0, 5 and 9). Paying in between cancels all of it. Nothing of this needs you.

8. Upgrade

Three things get upgraded, each its own way. All three are started by you, from the kit or the console; nothing updates by itself.

The ONE box itself (about 30 seconds):

cargo xtask self-release --where upcloud --box test2 --publish      # build and publish the next app (about 11 min)
cargo xtask box update --where upcloud --box test2 --apply          # the box takes it

Or in the console: Sites → "This box" → Update this box to . The box stages the new app into its other slot, restarts itself, serves 60 seconds to confirm, and rolls back by itself if it is not serving within 10 minutes. Your data is never touched. If the kit says Base: use upgrade, the base system changed and the fast path cannot do it: run cargo xtask upgrade --where upcloud --box test2 --apply instead (the slow one, about 17 minutes, a re-install that keeps /data).

The plugins (a new product guest, a new Stripe or cloud plugin):

cargo xtask release --where upcloud --box test2 --sign-with ~/keys --apply --publish
cargo xtask box plugins --where upcloud --box test2 --apply

Or Sites → "Plugins" → Update plugins. The table says per plugin: unchanged, replaced, held (a product you have no site of yet; it is taken at the first site) or refused (the signature did not verify: nothing of it is written). A replaced plugin is live within 2 seconds; nothing restarts.

A site or a customer's estate (the product's new release):

cargo xtask media --where upcloud --box test2 --kind dev --apply      # the new release media
cargo xtask feed --where upcloud --box test2 --apply
cargo xtask estate update test2-gunnar-rs --where upcloud --box test2
cargo xtask estate update-status test2-gunnar-rs --where upcloud --box test2

Or on the site's row: Update to . The estate's own front pulls the update and updates the twin first, then the main, then itself last; a failure stops the update before the next box. With "Live updates — on" a box takes a new release of the same base without rebooting; a base change is a reboot from the keep medium (minutes). Do the test site first, the selling site when the test site is green. cargo xtask publish gunnar does all of these steps in order.

9. Terminate

10. When something is wrong