rustify

the runbook

From idea to a product people can buy.

The exact path Rustify uses to turn a native Rust app into a monetized, licensed, notarized download — the same one that took our own apps from scratch to a live storefront. It's two parts: open your accounts once, then repeat a thin per-product loop.

App repo your Rust app · rustify.toml · secrets.env
Landing repo Astro storefront + Paddle→Keygen webhook · .env

One rustify.toml is the source of truth; secrets stay in gitignored env files and get fanned out to where they're consumed.

part 0 · do once

Open your accounts — once.

Account- and identity-level setup, shared by every product you ship. You bring your own Paddle and Apple accounts — Rustify never touches your money or your signing keys. Do this once; reuse it forever.

ServiceWhat you getPowers
Keygenaccount · admin token · account id (public)in-app validation + minting keys
Paddleaccount · API key · client-side tokencheckout + the webhook's email lookup
Cloudflareaccount · API token (Pages + Zone→DNS + R2, all Edit) · account iddeploy landings, custom domains, R2 downloads
AppleDeveloper Program membershipthe code-signing identity
Postmarkaccount · server token · account tokenemailing license keys (optional)
A domainregistered, DNS zone in your Cloudflare accountevery product's subdomain

Local tooling — install once

# scaffold (step 1) + package the .app (release)
cargo install cargo-generate cargo-packager

Plus node/npm + wrangler (landing deploy) and curl + jq (every script).

Apple signing

Install your Developer ID Application cert in the keychain and store a notarytool profile once. Every product signs with the same identity.

Paddle go-live

Complete seller verification, add payout details, set a default payment link. Account-level — required before any live checkout.

Cloudflare downloads

Create one R2 bucket for your .dmgs, bind a downloads.yourdomain custom domain, add a cache-bypass rule for /*.dmg. All products publish here.

Verify your sender

Run setup-email.sh once — it writes DKIM + Return-Path into Cloudflare and verifies in Postmark. Then every product emails keys from your domain.

Two gotchas that cost real time: the Cloudflare permission you want is Zone → DNS → Edit, not the similar-looking "Account DNS Settings" (which can't write records). And in Paddle, "Notification settings" is a different permission from "Notifications".

part 1 · per product

Then repeat the loop.

Reuses everything above — same accounts, same cert, same verified sender, same downloads bucket. A new product creates resources inside those accounts; it never needs new ones. Every script takes DRY_RUN=1.

  1. Scaffold the app

    app automated

    A buildable app pre-wired to the licensing SDK, the release pipeline, and its own scripts/.

    cargo generate --git https://github.com/hery/rustify templates/egui
  2. Build your product

    app manual

    Your idea. One binary runs locked (free tier) and unlocks at runtime when a license validates — gate paid code behind unlocked().

  3. Stand up the landing

    landing setup

    Copy web/ into a landing repo, edit site.config.ts (name, price, domain), copy .env.example → .env.

  4. Fill config

    app manual

    Set rustify.toml (name, bundle id) and secrets.env — paste your existing Part 0 tokens, set the price + targets. No new accounts.

  5. Create this product's resources

    app automated

    A new Keygen product → policy → token and Paddle product → price → webhook, created inside your existing accounts.

    ./scripts/provision-resources.sh
  6. Wire the Paddle client token

    manual

    Paste your account-level live_… token into the landing .env. Same token works for every product.

  7. Fan secrets out

    app automated

    Push webhook secrets + PUBLIC_PADDLE_* to this product's Cloudflare Pages project and its GitHub repo.

    ./scripts/provision.sh
  8. Deploy the storefront

    landing automated

    Build + deploy to Cloudflare Pages, register the custom domain, write the CNAME. The webhook deploys with it.

    bash /path/to/rustify/web/deploy.sh
  9. Build & publish the app

    app automated

    Build → codesign → notarize → staple → publish App.dmg to R2, signed with your Part 0 cert.

    ./scripts/release.sh
  10. Verify it's live

    app automated

    A read-only doctor: landing 200/TLS, checkout inlined, webhook rejects unsigned, Keygen reachable, price active.

    ./scripts/preflight.sh
  11. Approve the domain in Paddle

    manual

    The one per-product compliance gate: submit this product's domain for website approval. Until it clears, live checkout is blocked.

the honest part

What stays manual — and why.

One-time (Part 0)

Opening accounts, issuing tokens, installing your signing cert, Paddle seller verification + payout. Identity and billing — only you can.

Per product (Part 1)

Writing your app, and website approval for the new domain. That's the whole manual layer per product — everything else is a script.

Rustify never centralizes the two things that would make it a regulated liability — being the Merchant-of-Record (your Paddle) and custodying signing keys (your keychain/CI). Those gates are manual on purpose.