Three ways to run klotto's ticket-buying and raising as their own process — and the one that turns buying a lottery ticket into a deal a KEA notary signs.
The short version. Option A is an afternoon and gets the bots off the backend. Option C is a day or two, needs no new trust story, and is the only one that exercises the thing you actually want exercised — a stranger raising a transaction and a notary deciding whether to sign it. I'd do A now and C next, and skip B.
Option C is set up and tested offline (KLOTTO-TICKETDEAL1). klotto now publishes a fifth deal
per pot, testnet-<cc>-ticket.v2 — a v2 policy for buying one ticket: { player, stake,
ref }, the pot must be open, the stake in range. The bots raise it unsigned with
KLOTTO_TICKETS=deal; a player-side notary signs it with
kea/config/klotto.player.testnet.json and the player's own hourly budget. The offline
proof is qa/klotto/ticket-tests.js in hbarlive_hts. The converter
covers the v1 → v2 half of the queue. Still to do by hand: put a player key on a device and run
kea go against that config — the live test needs the key, which is yours to place.
Everything lives in one pm2 fleet on the GCP VM, declared in ecosystem.kbot.config.js. The klotto pieces are two of about ten apps in that file:
| pm2 app | script | does | needs keys |
|---|---|---|---|
| katdemo-klotto-pilot | kbot/klotto/klotto-bots.js | Buys tickets as alice/bob/carol; raises claim and unlock when the chain says one is due | Yes — the player accounts |
| katdemo-kea-pilot | kea/node.js | The box's own notary signer | Yes — the house keys |
Two consequences worth naming before choosing anything. First, the raiser and a signer sit on the same machine as the backend and the pool, so one box compromise takes the whole loop — fine on testnet, not a mainnet shape. Second, pm2 resurrect brings both back on reboot, which is why the box signer reappeared after you stopped it.
It is two unrelated jobs wearing one name, and that is the seam to cut along.
/katomia/deal-bytes with a deal template and its bindings, kpool:true. Holds no key. Signs nothing.The raiser is already key-less: it hands the backend a template and parameters, the backend builds the bytes and pools them, and notaries elsewhere decide. That is why moving it is cheap — it is a client, not an authority.
Same file, its own process, its own config, run anywhere with outbound HTTPS. The work is removing its two remaining dependencies on being inside the repo on the box:
resolve treatment KEA now has — a directory of <dealId>.json, verified by hash — or let it fetch klotto.kpay.uk/deals/index.json and verify.http://127.0.0.1:8030. Point it at https://api.katomia.com/hapi, which now works from anywhere.Then it is klotto.config.json beside a checkout, gitignored, exactly like kea.config.json. Runs on a laptop, a phone, a Pi.
Cost: an afternoon. Buys: the demo stops depending on the backend box; you can run the players from anywhere and watch the notaries react.
Two processes: one holds player keys and buys, one holds nothing and raises. Clean on paper, and it is what you would do at scale.
But the split already exists inside the file, the two jobs share the pot state they both read from the mirror, and separating them now means two configs, two deploys and a new coordination question for no behaviour you cannot already get from A. Do it when there is a second application raising, not before.
This is the exercise you described, and it is worth doing for its own sake.
Today a ticket purchase is a transfer the player signs itself. Instead: publish a ticket deal — a v2 policy that says "this account may move exactly the ticket price from a player account to the pot, provided the pot is open and the amount is one of the allowed stakes" — and have the buyer raise it unsigned, like any other deal. A KEA notary holding the player key then decides and signs.
What that gets you that nothing else does:
Cost: a day or two — most of it writing and pinning the ticket policy, which is the same shape as the existing claim policy but simpler (no timelock, no NFT, no digit match).
Ticket volume vs. the pool's window. The pool keeps transactions about an hour and the dashboard reads a live window. Players buying every few seconds through the pool would swamp both. Option C probably wants a slower arrival rate, or a shorter valid window so tickets clear quickly.
Who holds the player keys under C? If the player's notary runs beside the buyer, you have moved the key, not removed it. The honest version has the buyer holding nothing and a separate notary — possibly on a phone — holding the player key. That is more moving parts, and it is also the whole point.
The BR claims are expiring unsigned, and it is a config mismatch, not the bots. The raiser declares c9ce3a30… — testnet-BR-claim, the v1 document. Your phones' kea.config.json lists 4b510865… — testnet-BR-claim.v2. Different deal, so the notaries correctly ignore it: zero verdicts, zero signatures, expired. Watch both ids, or swap to the v1 one the bot actually raises.
kea.config.json already does. No new concepts.Three things are waiting on roughly the same groundwork — a demo that runs as its own process, off the backend box, against a published deal.
The reward bot is already in the same pm2 fleet (katdemo-reward-pilot, kbot/reward-bot.js) and is in exactly the position klotto's bots are in: works, lives on the box, has no config of its own. It is the natural second customer for whatever Option A produces — same extraction, same shape, and it would prove the pattern generalises rather than being a klotto special.
Further out and larger. The zone is active in the Cloudflare account and unattached — so unlike xbar it needs a Worker pointed at it, not a zone move. The blocker is not hosting: the register has /domain/query down as needing a DNS-verified rebuild, and tx, kwt and the monitor all depend on xproof identity and cannot be re-enabled until it answers. That is a build, not an extraction, and it should follow the two above.
A notary on a phone in Termux is not a durable host — your own console shows the doorbell dropping and reconnecting every two minutes or so. That belongs in its own note, and it is the companion to this one.