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:
- A product is what you sell: Gunnar (a git server) or Holger (an artifact server). The ONE box knows a product only through a small signed program the product ships, its guest (
gunnar_monetize_guest.wasm,holger_monetize_guest.wasm). You never write one; the kit feeds it. - A site is your own public face of a product, for example
www.gunnar.rs. It is a set of three servers (a front, a main, a twin) the ONE box lays for you, exactly as it lays a customer's. Customers buy on the site; the site hands every buy to the ONE box. - An estate is one customer's set of those three servers, bought on a site. "A private Gunnar." The ONE box lays it, pairs it, and the customer gets a sign-in link.
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):
- the kit:
git clone ssh://git@git.gunnar.rs:2222/vetra/private-monetize-ops, Rust, terraform; - an UpCloud account and its API token, in a vault file under your home directory, mode 0600 (the kit refuses a looser file);
- a Stripe account, a key pair for the plugin signatures (
plugin-signing.pk8+plugin-signing.der) and the korp root seed that signs the installer media: the release manager gives you these. They never go onto the box. - the signed plugins in a directory (
$MONETIZE_SOLO_ARTIFACTS): every product guest,stripe.wasm,upcloud.wasm,dns_cloudflare.wasm, each with its.p7sbeside it.
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:
- Test buy in the console. One button per product and site:
hosted-10gb · test2.gunnar.rsorprivate-small · test2.gunnar.rs. On a Stripe test key the box pays with Stripe's test card by itself; a real account (and, forprivate-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. - 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
- A customer's estate: the customer does it on the site (a fresh login, the name typed back). You can do it for them: the estate's Guardian page, or
cargo xtask site terminate <name> --confirm <name> --where upcloud --box test2. Machines, disks and DNS names go; nothing is refunded; no backup is taken, ever. - Your own site: the same, but only if its declaration says
terminable = true; otherwise the button is not there and the verb refuses. - The ONE box:
cargo xtask downworks only on the local mock; a real box is never taken down by the kit. Stop it at the cloud when you mean it; the daily UpCloud backup and the off-box copy (every 6 hours, sealed with age to your key) are what you restore from:cargo xtask restore --from upcloud-backupor--from offsite --opened <dir>.
10. When something is wrong
- The console's red banner and the alert mail name the case and the next step.
- Guardian lists every estate with its main and twin and any open repair case. A box that merely stopped is started again by itself. A lost twin or main is a case: the page offers Recharge from twin, Review twin, New twin, New server; the recommended one is red. Nothing is rebuilt until you press, unless you turned auto-fix on for that estate. A main is never replaced by its twin: a new main is laid and filled from the twin.
cargo xtask logs --where upcloud --box test2shows the box's own log;monetize box runningwhat it runs; Iron what you own at the cloud and what no order owns.- A site that does not answer after a lay:
cargo xtask site status <site>, thensite recheck,site retry, andsite open-pin <site>if the alert says a box laid again was not given its pin window.