<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[SunLicense Guides]]></title><description><![CDATA[SunLicense Guides]]></description><link>https://sunlicense-guides.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6aee31bcade7973ade3b2b94/99cc6b2e-80b5-487c-aeb2-8468939f7e2e.png</url><title>SunLicense Guides</title><link>https://sunlicense-guides.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 12 Nov 2026 07:23:16 GMT</lastBuildDate><atom:link href="https://sunlicense-guides.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to sell your first plugin, script or app: a beginner's checklist]]></title><description><![CDATA[Selling your first plugin, script, bot or app? Pick a store, set a price, add license keys, and get refunds, support and updates right before launch.]]></description><link>https://sunlicense-guides.hashnode.dev/how-to-sell-your-first-plugin-script-or-app-a-beginner-s-checklist</link><guid isPermaLink="true">https://sunlicense-guides.hashnode.dev/how-to-sell-your-first-plugin-script-or-app-a-beginner-s-checklist</guid><category><![CDATA[beginners guide]]></category><category><![CDATA[indiehackers]]></category><category><![CDATA[side project]]></category><category><![CDATA[GameDev]]></category><dc:creator><![CDATA[Kasun Hapangama]]></dc:creator><pubDate>Fri, 06 Nov 2026 21:01:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6aee31bcade7973ade3b2b94/6573eb84-9674-4783-838d-08d757008d6a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You built something people keep asking for: a Minecraft plugin, a FiveM script, a Discord bot, a small desktop tool. Friends say you should charge for it. Then the questions pile up. Where do I sell it? How much? What stops the first buyer from sharing it? What do I do when someone wants a refund?</p>
<p>This is the checklist I'd give anyone selling their first piece of software. It's in the order you'll need it, and every step works whatever you sell and wherever you sell it.</p>
<p>I build SunLicense, a licensing service for people who sell plugins, scripts, bots and apps, and I use it as the worked example in the protection, refund and support steps.</p>
<h2>1. Make sure it's ready to sell</h2>
<p>Free users forgive rough edges. Paying users email you about them. Before you list it:</p>
<ul>
<li><strong>Install it on a clean machine or server</strong>, from the file you'll sell, following only your own README. Fix everything that trips you up.</li>
<li><strong>Give it a version number</strong> (<code>1.0.0</code>) and keep a short changelog from now on.</li>
<li><strong>Write down what it needs</strong>: game or server versions, Java, Node.js or Python version, operating systems.</li>
<li><strong>Decide what's included</strong>: is the source included? Can buyers modify it? Can they use it on more than one server? That becomes your license terms.</li>
</ul>
<h2>2. Pick where to sell it</h2>
<p>Where your buyers already shop matters more than anything else. A Minecraft server owner looks on Minecraft marketplaces; a developer buying a desktop tool is happy with a checkout link on your own site.</p>
<table>
<thead>
<tr>
<th>You sell</th>
<th>Good places to start</th>
</tr>
</thead>
<tbody><tr>
<td>Minecraft plugins and resources</td>
<td><strong>BuiltByBit</strong>, <strong>Voxel Shop</strong> (formerly Polymart), SpigotMC premium resources</td>
</tr>
<tr>
<td>FiveM and other game-server scripts</td>
<td><strong>Tebex</strong> (FiveM's official store)</td>
</tr>
<tr>
<td>Discord bots, desktop apps, CLI tools</td>
<td><strong>Gumroad</strong>, <strong>Lemon Squeezy</strong>, <strong>Payhip</strong>, <strong>Ko-fi</strong>, <a href="http://itch.io"><strong>itch.io</strong></a></td>
</tr>
<tr>
<td>Anything, on your own website</td>
<td><strong>Stripe</strong> Payment Links or Checkout</td>
</tr>
</tbody></table>
<p>Before you pick, read each store's rules for your kind of product (many have rules on DRM and licensing) and check who handles sales tax and VAT. Marketplaces bring buyers; your own site keeps more control.</p>
<h2>3. Set a price</h2>
<p>There's no formula, but there is a method:</p>
<ol>
<li><strong>Look at similar products</strong> in the same store, and note what they charge and what they include.</li>
<li><strong>Price for the problem you solve</strong>, not the hours you spent. A plugin that saves a server owner a weekend is worth more than one that adds a nice hat.</li>
<li><strong>Choose one-time or subscription.</strong> One-time is the norm for plugins and scripts; subscriptions fit products with ongoing costs or frequent updates (a hosted service, a bot with an API behind it).</li>
<li><strong>Keep a free version or a trial</strong> if you can. It's the best advert you have.</li>
<li><strong>Don't start too low.</strong> Raising a price later is harder than running a launch discount now.</li>
</ol>
<h2>4. Write a listing that answers questions before they're asked</h2>
<p>A good listing has: one sentence on what it does, a short video or 3-5 screenshots, a feature list, requirements (from step 1), how installation works, how to get support, your refund policy, and your terms (from step 1). Every question your listing answers is a support message you don't get.</p>
<h2>5. Protect it: one key per buyer, a check on start-up</h2>
<p>The first leak usually isn't a cracker. It's one buyer sharing their download. What stops most of it: every buyer gets their own license key, your product checks it when it starts, and each key only runs on a few machines. Here's how that looks with <a href="https://sunlicense.com/?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=guides-2026-11&amp;utm_content=sell-your-first-product">SunLicense</a> (free for 3 products and 100 licenses), step by step.</p>
<p><strong>5a. Create a product and a policy.</strong> Sign up (Google, GitHub, Discord or email), create a product, then a <strong>policy</strong>: what one purchase allows. Set <strong>Max machines</strong> to fit what you sell: 1-3 servers for a plugin or script, 2-3 machines for a desktop app. A shared key runs out of slots; buyers who move hosts can free one.</p>
<img src="https://raw.githubusercontent.com/sunlicense-com/guides/main/images/hashnode/04-step-policy.png" alt="Create a policy with a machine limit" style="display:block;margin:0 auto" />

<p><strong>5b. Connect your store.</strong> On the <strong>Integrations</strong> page, pick where you sell. Each sale then creates a license for that buyer, without you touching it:</p>
<table>
<thead>
<tr>
<th>Store</th>
<th>How the buyer gets their key</th>
</tr>
</thead>
<tbody><tr>
<td><strong>BuiltByBit</strong></td>
<td>An <em>External license key</em> placeholder writes each buyer's own key into the jar they download. Map the resource ID to your policy, then publish an update so new downloads carry it</td>
</tr>
<tr>
<td><strong>Voxel Shop</strong></td>
<td>The key is signed into the download, and purchases and refunds arrive by webhook</td>
</tr>
<tr>
<td><strong>Tebex</strong></td>
<td>A purchase creates the license; refunds and lost disputes revoke it</td>
</tr>
<tr>
<td><strong>Stripe, Gumroad, Lemon Squeezy</strong></td>
<td>The buyer sees the key after paying or in the receipt</td>
</tr>
</tbody></table>
<p>Payhip, Ko-fi, <a href="http://itch.io">itch.io</a>, SpigotMC and others are on the same page.</p>
<img src="https://raw.githubusercontent.com/sunlicense-com/guides/main/images/hashnode/04-step-integrations.png" alt="Connect your store on the Integrations page" style="display:block;margin:0 auto" />

<p><strong>5c. Add the check to your product.</strong> Use the SDK for your language (Java, JavaScript, Python, .NET, PHP or Lua), or let your coding agent do it: <strong>Products → your product → Setup → Copy prompt</strong>, paste it into Claude Code, Cursor or Codex, and it adds the check and tests it with a test license. Whichever way, follow the one rule that keeps buyers happy: <strong>only stop on a clear "no"</strong> (suspended, revoked, expired, too many machines). If the license server can't be reached, keep running.</p>
<p><strong>5d. Obfuscate the release build.</strong> <strong>Developers → Obfuscation</strong> takes a Java jar (ProGuard, Skidfuscator, or both with <em>Maximum</em>), JavaScript, Python or a zipped FiveM resource, 3 free runs a month. It makes the check and your code harder to find and patch. It slows people down; it doesn't stop a determined cracker.</p>
<h2>6. Decide how refunds work, and let them revoke the key</h2>
<p>Write your refund policy down (for example: within 7 days if it doesn't work on your server and support couldn't fix it), put it in the listing, and stick to it.</p>
<p>Then make sure a refund also takes the license away, or every refund is a free copy. With the store connected in 5b, Tebex, Voxel Shop, Stripe, Gumroad and Lemon Squeezy refunds <strong>revoke the key automatically</strong>: the buyer's copy stops at its next check, and everyone else is untouched. BuiltByBit's placeholder carries no refund information, so there you revoke the key yourself from the license page when you refund.</p>
<img src="https://raw.githubusercontent.com/sunlicense-com/guides/main/images/hashnode/04-step-licenses.png" alt="Every buyer's license, with keys masked and a status you can change" style="display:block;margin:0 auto" />

<p>The <strong>Licenses</strong> tab shows every buyer, their policy and status. Suspend a key while you sort out a dispute, reinstate it when it's resolved, or revoke it for good.</p>
<h2>7. Set up support before the first sale</h2>
<ul>
<li><strong>One place for support</strong>: a Discord server is the norm for plugins, scripts and bots; email works for apps.</li>
<li><strong>A buyer role</strong> in that Discord. The SunLicense Discord bot (on every plan) posts a claim panel: buyers enter their key and purchase email, get your "Customer" role, and lose it if the license is revoked or expires.</li>
<li><strong>A short FAQ</strong> with the five questions you expect (installation, where the key goes, moving to a new server, updates, refunds). Add to it every time you answer something twice.</li>
<li><strong>Say how fast you reply</strong> ("within a day"), and keep to it.</li>
</ul>
<h2>8. Plan your updates</h2>
<ul>
<li><strong>Ship small updates often</strong>, with the changelog in each one. Buyers judge a product by how alive it looks.</li>
<li><strong>Don't break existing setups.</strong> Keep config files compatible and say clearly when they aren't.</li>
<li><strong>Keep old license keys working</strong> across versions, so updating never means asking for a new key.</li>
</ul>
<h2>Launch-day checklist</h2>
<img src="https://raw.githubusercontent.com/sunlicense-com/guides/main/images/hashnode/04-launch-checklist.png" alt="Launch-day checklist" style="display:block;margin:0 auto" />

<p>The last item, a test purchase of your own product, catches more problems than everything else on the list.</p>
<h2>Get started</h2>
<p>Everything in steps 5 to 7 is on SunLicense's free plan: connect your store, add the check, obfuscate the release, and post the claim panel in your Discord. It covers 3 products and 100 licenses, with store integrations, SDKs, obfuscation and the Discord bot, no credit card and no cut of your sales. <a href="https://sunlicense.com/?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=guides-2026-11&amp;utm_content=sell-your-first-product-end">See how it works</a>, then work down the launch-day checklist.</p>
]]></content:encoded></item><item><title><![CDATA[How to protect your plugin or app from piracy, step by step]]></title><description><![CDATA[A practical anti-piracy plan for plugins, scripts, bots and apps: per-buyer keys, limits, a safe check, core unlock, obfuscation, watermarks, alerts.]]></description><link>https://sunlicense-guides.hashnode.dev/how-to-protect-your-plugin-or-app-from-piracy-step-by-step</link><guid isPermaLink="true">https://sunlicense-guides.hashnode.dev/how-to-protect-your-plugin-or-app-from-piracy-step-by-step</guid><category><![CDATA[Security]]></category><category><![CDATA[GameDev]]></category><category><![CDATA[indiehackers]]></category><category><![CDATA[Java]]></category><dc:creator><![CDATA[Kasun Hapangama]]></dc:creator><pubDate>Fri, 06 Nov 2026 20:40:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6aee31bcade7973ade3b2b94/5aa8b59a-2a44-41ae-bb4d-c2c1276e0a97.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Sell a Minecraft plugin, a FiveM script, a Discord bot or a desktop app, and someone will use it without paying.</strong> Usually it isn't a skilled cracker. It's one buyer sharing their copy with five friends, or a jar reposted on a leak forum the week you launch.</p>
<p>You can't make piracy impossible, and trying to usually hurts paying customers more than pirates. What you can do is make sharing inconvenient, make leaks traceable, and make paying the easy option. This is the plan I'd follow, in order. Each step is worth doing on its own, so stop wherever the effort stops paying off.</p>
<h2>Step 0: Decide what you're actually fighting</h2>
<table>
<thead>
<tr>
<th>Threat</th>
<th>How common</th>
<th>What stops it</th>
</tr>
</thead>
<tbody><tr>
<td>A buyer shares their copy or key</td>
<td>Very common</td>
<td>Per-buyer keys and machine limits (steps 1-2)</td>
</tr>
<tr>
<td>A jar gets reposted on a leak site</td>
<td>Common</td>
<td>Per-buyer keys, watermarks, takedowns (steps 1, 6, 8)</td>
</tr>
<tr>
<td>Someone deletes your license check</td>
<td>Less common, easier with AI editors</td>
<td>Core unlock and obfuscation (steps 4-5)</td>
</tr>
<tr>
<td>A determined cracker rewrites your code</td>
<td>Rare</td>
<td>Nothing fully. Make it slow and not worth it</td>
</tr>
</tbody></table>
<p>Most losses sit in the first two rows, and those are the cheapest to fix.</p>
<h2>Step 1: Give every buyer their own key, automatically</h2>
<p>A single shared password or license file can't tell anyone apart. Give each purchase its own key, so you can see who's using what and switch off exactly one person.</p>
<p>Don't do it by hand. Connect your store so keys are created at checkout. On BuiltByBit, an <em>External license key</em> placeholder writes the buyer's key into the jar they download. Voxel Shop and SpigotMC have their own placeholders, and Tebex, Stripe, Gumroad, Lemon Squeezy and others can create a key through a webhook. Wire refunds in too, so a refunded purchase loses its key.</p>
<h2>Step 2: Limit where a key can run</h2>
<p>A key that works everywhere is a key that gets shared. Set a limit that fits the product:</p>
<ul>
<li><strong>Plugins:</strong> servers per key (1-3 is typical).</li>
<li><strong>Desktop apps:</strong> machines per key (2-3 covers a laptop and a desktop).</li>
<li><strong>Bots or web tools:</strong> an IP address limit.</li>
</ul>
<p>When the limit is reached, a new machine is refused with a clear message, and the buyer can free a slot themselves. Be generous: a support ticket costs you more than one extra machine.</p>
<img src="https://raw.githubusercontent.com/sunlicense-com/guides/main/images/devto/anti-piracy-policy.png" alt="A policy with Max machines and Max IPs per key" style="display:block;margin:0 auto" />

<h2>Step 3: Check at startup, and never take a customer down</h2>
<p>Check the license when the app starts, then again every few hours. No need to phone home every minute. Above all, <strong>only stop on a clear "no"</strong> (revoked, suspended, expired, too many machines). If your license server can't be reached, keep running. BuiltByBit's DRM guidance says the same, and plugins that disable themselves when a server is down risk being removed. For a Minecraft plugin, I walk through this check line by line in <a href="https://dev.to/ka0/how-to-add-license-keys-to-a-minecraft-plugin-in-10-minutes-bd6">How to add license keys to a Minecraft plugin in 10 minutes</a>.</p>
<p>For customers without internet, ship a <strong>signed offline license</strong>: a file signed with a private key (Ed25519, for example) that your app verifies with the public key, and that expires on a date you set.</p>
<h2>Step 4: Make the check hard to delete</h2>
<p>A license check is one <code>if</code> statement. Anyone with a decompiler, or an AI editor, can delete it. Two things make that pointless:</p>
<ul>
<li><strong>Core unlock.</strong> Encrypt a small, essential piece of your code (a core class, default rules, a lookup table) and only decrypt it after a valid license check. Delete the check, and that piece is simply missing.</li>
<li><strong>Tamper reports.</strong> Hash your own classes and report a modified copy with the next license check, so you know which license runs a patched build.</li>
</ul>
<h2>Step 5: Obfuscate your release builds</h2>
<p>Rename classes and methods, encrypt strings and scramble control flow, so your check and your core are hard to find and patch. ProGuard gives a safe rename; ProGuard followed by Skidfuscator adds control flow and string encryption. Only obfuscate the build you sell, test it before upload, and keep the mapping file so you can read customers' stack traces. I wrote a step-by-step guide: <a href="https://dev.to/ka0/how-to-obfuscate-a-minecraft-plugin-free-step-by-step-283m">How to obfuscate a Minecraft plugin</a>.</p>
<h2>Step 6: Watermark every download</h2>
<p>Put something in each buyer's copy that names them: their license key in the jar, an ID in the manifest, a mark in the archive. When the jar shows up on a leak site, you know whose it was without guessing. It also deters sharing: people think twice when their copy carries their name.</p>
<h2>Step 7: Watch for sharing</h2>
<p>You won't spot a shared key by reading logs. Get alerts for the patterns that matter: one key on many machines at once, a key checking in from two countries an hour apart, a machine trying random keys, a revoked key still in use, a key pasted in your Discord. Then act on the real ones (a customer who travels isn't a pirate).</p>
<img src="https://raw.githubusercontent.com/sunlicense-com/guides/main/images/devto/anti-piracy-sentinel.png" alt="Sentinel watches license checks and flags shared and leaked keys" style="display:block;margin:0 auto" />

<h2>Step 8: Respond fast when something leaks</h2>
<ol>
<li>Find the buyer from the watermark or the key in the leaked file.</li>
<li>Revoke that key. Copies stop at their next check; everyone else is untouched.</li>
<li>Send a takedown notice (DMCA) to the site hosting it. Most marketplaces also have a DMCA form.</li>
<li>Revoke refunded and charged-back purchases too, or they keep a free copy.</li>
</ol>
<h2>Step 9: Make paying easier than pirating</h2>
<p>The best anti-piracy feature is a product that's easy to buy. A fair price, a trial, fast updates, real support and a Discord. A pirate gets an old jar and no help. A customer gets the update that fixes their bug today.</p>
<h2>The whole plan on one page</h2>
<img src="https://raw.githubusercontent.com/sunlicense-com/guides/main/images/devto/anti-piracy-layers.png" alt="The plan, layer by layer: keys, limits, a safe check, core unlock, obfuscation, watermarks, alerts, response" style="display:block;margin:0 auto" />

<h2>How to put it together</h2>
<p>You can build each layer yourself: a small key server, a signing key, an obfuscation step in your build, and a dashboard to revoke from. Or use a service. I build <a href="https://sunlicense.com/?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=guides-2026-11&amp;utm_content=anti-piracy-plan">SunLicense</a>, which covers the list:</p>
<table>
<thead>
<tr>
<th>Step</th>
<th>In SunLicense</th>
<th>Plan</th>
</tr>
</thead>
<tbody><tr>
<td>1. Per-buyer keys from your store</td>
<td>BuiltByBit, Voxel Shop, SpigotMC, Tebex, Stripe, Gumroad and more</td>
<td>Free</td>
</tr>
<tr>
<td>2. Machine and IP limits</td>
<td>Per policy</td>
<td>Free</td>
</tr>
<tr>
<td>3. Safe startup check, offline files</td>
<td>SDKs for Java, JavaScript, Python, .NET, PHP and Lua; signed Ed25519 files</td>
<td>Free</td>
</tr>
<tr>
<td>4. Core unlock and tamper reports</td>
<td>Seal up to 512 KB, 7 days offline grace</td>
<td>Free</td>
</tr>
<tr>
<td>5. Obfuscation</td>
<td>Maximum, ProGuard, yGuard, Skidfuscator; JavaScript, Python, Lua (FiveM)</td>
<td>Free (3 runs a month)</td>
</tr>
<tr>
<td>6. Watermarked downloads</td>
<td>The buyer's mark in each file</td>
<td>Standard</td>
</tr>
<tr>
<td>7. Sharing alerts</td>
<td>Sentinel detectors, Discord alerts</td>
<td>Free</td>
</tr>
<tr>
<td>8. Revoke in one click</td>
<td>Dashboard, CLI, Discord</td>
<td>Free</td>
</tr>
</tbody></table>
<p>It's free for 3 products and 100 licenses, with no credit card and no cut of your sales. <a href="https://sunlicense.com/?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=guides-2026-11&amp;utm_content=anti-piracy-plan-end">See what SunLicense covers</a>, then work through the list, starting with step 1.</p>
]]></content:encoded></item><item><title><![CDATA[How to sell a Discord bot with license keys]]></title><description><![CDATA[Sell a self-hosted Discord bot: a license key per buyer from Stripe, Gumroad or Lemon Squeezy, a check in discord.js or discord.py, roles that follow it.]]></description><link>https://sunlicense-guides.hashnode.dev/how-to-sell-a-discord-bot-with-license-keys</link><guid isPermaLink="true">https://sunlicense-guides.hashnode.dev/how-to-sell-a-discord-bot-with-license-keys</guid><category><![CDATA[discord]]></category><category><![CDATA[Discord bot]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Kasun Hapangama]]></dc:creator><pubDate>Fri, 06 Nov 2026 20:36:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6aee31bcade7973ade3b2b94/6c00380e-aaef-4503-93ff-c770c8ab5eaa.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You wrote a Discord bot that server owners want to run themselves: a ticket system, a moderation suite, an economy bot. You'd like to charge for it. The problem shows up with the second sale: the first buyer zipped the folder and shared it, and now it's running in servers that never paid.</p>
<p>This guide walks through selling a self-hosted bot with a license key per buyer: issuing keys when people pay, checking them in <code>discord.js</code> or <code>discord.py</code>, keeping the bot up when the licensing server is down, and giving buyers a role in your support server that follows their license.</p>
<p>I build SunLicense, the licensing service used in the examples. The pattern works with any licensing API.</p>
<p><strong>First, do you need this?</strong> If you host the bot yourself and sell access per server, you don't need license keys: you already control who it runs for. Keys are for bots that buyers download and run on their own machine or VPS.</p>
<h2>How it fits together</h2>
<img src="https://raw.githubusercontent.com/sunlicense-com/guides/main/images/hashnode/02-flow.png" alt="From payment to a running bot" style="display:block;margin:0 auto" />

<ol>
<li>A buyer pays in your store. The store tells the licensing service, which issues a key.</li>
<li>The buyer puts the key in the bot's <code>.env</code> next to their Discord token.</li>
<li>The bot checks the key when it starts and every few hours. Valid: it logs in. A clear "no": it stops.</li>
<li>A refund revokes the key (a chargeback suspends it); the next check stops the bot.</li>
</ol>
<h2>Step 1: create a product and a policy</h2>
<p>In SunLicense, create a <strong>product</strong> for the bot and a <strong>policy</strong> for what one purchase allows: for example a perpetual license with a limit of 1 or 2 machines. The machine limit is what turns a shared key into a useless one: once its slots are taken, the next server is refused, and buyers can free a slot themselves when they move hosts.</p>
<p>You also create an <strong>API key</strong> (<code>slk_...</code>) for the bot. It can only validate licenses, so it's the one that ships inside the bot. Never ship a personal access token.</p>
<h2>Step 2: issue a key per buyer from your store</h2>
<p>Connect your store on the <strong>Integrations</strong> page so every sale creates a license without you touching it:</p>
<table>
<thead>
<tr>
<th>Store</th>
<th>How the buyer gets the key</th>
<th>Refunds</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Stripe</strong> (Payment Links or Checkout)</td>
<td>After paying, the buyer is sent to a page that shows their key</td>
<td>A full refund revokes; a dispute suspends</td>
</tr>
<tr>
<td><strong>Gumroad</strong></td>
<td>Gumroad's own per-sale license key, in the receipt, is the SunLicense key</td>
<td>Revokes</td>
</tr>
<tr>
<td><strong>Lemon Squeezy</strong></td>
<td>Lemon Squeezy's license key, in the receipt, is mirrored to SunLicense</td>
<td>Revokes</td>
</tr>
</tbody></table>
<p>Payhip, Ko-fi, <a href="http://itch.io">itch.io</a>, Tebex and others work the same way. Selling to one customer by hand? Issue a license from the dashboard or from Discord with <code>/license issue</code>.</p>
<h2>Step 3: check the license when the bot starts</h2>
<p>The rule that keeps both you and your buyers happy: <strong>check on startup, re-check every few hours, stop only on a clear "no", and keep running when the licensing server can't be reached.</strong> An outage on your licensing provider's side should never take your customers' bots offline.</p>
<p>The SDK sends <code>POST /api/v2/license/validate</code>. HTTP 200 means valid, 400 means rejected (the body's <code>code</code> says why: SUSPENDED, REVOKED, EXPIRED, a full machine limit). Anything else, or a network error, means "couldn't check".</p>
<img src="https://raw.githubusercontent.com/sunlicense-com/guides/main/images/hashnode/02-check-rules.png" alt="Three answers, three reactions" style="display:block;margin:0 auto" />

<h3><code>discord.js</code></h3>
<pre><code class="language-js">// npm install sunlicense discord.js
import os from "node:os";
import { Client, GatewayIntentBits } from "discord.js";
import { SunLicense } from "sunlicense";

const sunlicense = new SunLicense({ baseUrl: "https://api.sunlicense.com", token: process.env.SUNLICENSE_API_KEY });
const machineId = os.hostname(); // or an id you generate once and store

async function checkLicense() {
  try {
    const r = await sunlicense.validate(process.env.LICENSE_KEY, { host: machineId });
    if (r.valid) return { ok: true };
    if (r.status === 400) return { ok: false, code: r.data?.code };   // a clear "no"
  } catch {}
  return { ok: true, unreachable: true };                            // fail open
}

const first = await checkLicense();
if (!first.ok) {
  console.error(`License rejected (${first.code}). The bot will not start.`);
  process.exit(1);
}

const client = new Client({ intents: [GatewayIntentBits.Guilds] });

setInterval(async () =&gt; {
  const s = await checkLicense();
  if (!s.ok) {
    console.error(`License rejected (${s.code}). Stopping.`);
    await client.destroy();
    process.exit(1);
  }
}, 6 * 60 * 60 * 1000);

await client.login(process.env.DISCORD_TOKEN);
</code></pre>
<h3><code>discord.py</code></h3>
<p>The Python SDK is synchronous, so the periodic check runs in a thread to keep the event loop free.</p>
<pre><code class="language-python"># pip install sunlicense discord.py
import asyncio, os, platform
import discord
from sunlicense import SunLicense

sunlicense = SunLicense(base_url="https://api.sunlicense.com", token=os.environ["SUNLICENSE_API_KEY"])

def check_license():
    """Returns None when the bot may run, or the rejection code."""
    try:
        r = sunlicense.validate(os.environ["LICENSE_KEY"], host=platform.node())
    except Exception:
        return None                                   # unreachable: fail open
    if r.valid or r.status != 400:
        return None
    return r.data.get("code") if isinstance(r.data, dict) else "REJECTED"

class Bot(discord.Client):
    async def setup_hook(self):
        self.loop.create_task(self.recheck())

    async def recheck(self):
        while True:
            await asyncio.sleep(6 * 60 * 60)
            code = await asyncio.to_thread(check_license)
            if code:
                print(f"License rejected ({code}). Stopping.")
                await self.close()
                return

code = check_license()
if code:
    raise SystemExit(f"License rejected ({code}). The bot will not start.")

Bot(intents=discord.Intents.default()).run(os.environ["DISCORD_TOKEN"])
</code></pre>
<p>A few details worth getting right:</p>
<ul>
<li><strong>The machine id.</strong> The examples use the host name to stay short. Something that survives reboots and moves better is an id you generate once and keep in a per-user folder; the template below does that.</li>
<li><strong>The API key in the build.</strong> While developing, <code>SUNLICENSE_API_KEY</code> comes from your own <code>.env</code>. In the copy you sell, write it in at build time (a bundler define or a generated settings file), so buyers only add their <code>LICENSE_KEY</code> and <code>DISCORD_TOKEN</code>.</li>
<li><strong>Logs.</strong> Print keys masked (<code>AX8P-XXXX-XXXX-XXXX</code>), never in full. Buyers paste logs into support tickets.</li>
</ul>
<p><strong>Don't want to write it?</strong> The <a href="https://github.com/sunlicense-com/example-discord-bot-js">discord.js template</a> has all of this built in, plus an offline license file, a <code>/license</code> status command, analytics and a sealed core that only opens with a valid license. There's also a <a href="https://github.com/sunlicense-com/example-discord-bot-jda">JDA template</a> for Java bots.</p>
<h2>Step 4: give buyers a role in your support server</h2>
<p>Most bot sellers run a Discord server for support and updates, and want a "Customer" role for people who paid. Doing that by hand doesn't scale and never gets undone after a refund.</p>
<p>The <a href="https://sunlicense.com/for/discord-bots?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=guides-2026-11&amp;utm_content=discord-bot-license-keys">SunLicense Discord bot</a> does it from the license itself:</p>
<ol>
<li>Connect your server from the dashboard's <strong>Discord</strong> page and run the setup command it gives you.</li>
<li><code>/sunlicense roles</code> picks the role for each product (or one verified-buyer role for any product).</li>
<li><code>/sunlicense panel</code> posts a claim panel. A buyer presses <strong>Claim a license</strong> and enters their key <strong>and</strong> purchase email: both must match, so a leaked key alone can't be claimed.</li>
</ol>
<img src="https://raw.githubusercontent.com/sunlicense-com/guides/main/images/hashnode/02-discord-card.png" alt="A license card in Discord, key masked" style="display:block;margin:0 auto" />

<p>The role follows the license: given on claim, removed when the license is revoked, suspended or expires, given back on reinstate. Buyers can see their licenses with <code>/my-licenses</code>, reveal their key privately and free a machine slot themselves.</p>
<p>Your staff get notifications too, including a <strong>key-sharing alert</strong> when a license is refused because all its machines are in use, with a button to suspend it.</p>
<h2>What this doesn't do</h2>
<p>A license check stops casual sharing: the zipped folder, the key passed around a server. Someone determined can still edit the check out of a JavaScript or Python bot. Obfuscating your release and sealing a core part of the bot (so it only runs after a valid check) make that slower; they don't make it impossible. Pick the effort that matches what the bot earns.</p>
<h2>Get started</h2>
<p>The Discord bot, the SDKs and store integrations are on every SunLicense plan, including the free one (3 products, 100 licenses, no credit card). The <a href="https://sunlicense.com/for/discord-bots?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=guides-2026-11&amp;utm_content=discord-bot-license-keys-end">page for Discord bot sellers</a> shows the whole setup, and the <a href="https://docs.sunlicense.com/guide/discord">Discord guide</a> lists every command.</p>
]]></content:encoded></item><item><title><![CDATA[How to add license keys to an app you built with an AI agent]]></title><description><![CDATA[Selling an app you built with Claude Code, Cursor or Codex? Add license keys with one prompt, prove the check works, and review five things yourself.]]></description><link>https://sunlicense-guides.hashnode.dev/how-to-add-license-keys-to-an-app-you-built-with-an-ai-agent</link><guid isPermaLink="true">https://sunlicense-guides.hashnode.dev/how-to-add-license-keys-to-an-app-you-built-with-an-ai-agent</guid><category><![CDATA[AI]]></category><category><![CDATA[ai agents]]></category><category><![CDATA[claude-code]]></category><category><![CDATA[indiehackers]]></category><dc:creator><![CDATA[Kasun Hapangama]]></dc:creator><pubDate>Fri, 06 Nov 2026 20:32:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6aee31bcade7973ade3b2b94/c2e524bf-c72b-4bbf-8179-0ffcdcb4e927.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You built the app with Claude Code, Cursor or Codex in a weekend. It works, people want it, and you're about to sell it on Gumroad, Lemon Squeezy or a Stripe Payment Link. Then the obvious question: what stops the first buyer from sending the download to ten friends?</p>
<p>A license key. Each buyer gets their own key, the app checks it when it starts, and you can switch a key off after a refund or when it shows up on a forum. This guide shows how to add one with a single prompt to the same agent that wrote the app, how the agent proves it works, and what you should still check by hand.</p>
<p>I build SunLicense, the licensing service used as the example here. The checklist at the end applies to any licensing setup.</p>
<h2>What a license check needs to do</h2>
<p>Before you let an agent loose on it, know what "done" looks like. A good license check:</p>
<table>
<thead>
<tr>
<th>Part</th>
<th>What it means</th>
</tr>
</thead>
<tbody><tr>
<td>One key per buyer</td>
<td>Issued automatically when someone pays, delivered with the receipt</td>
</tr>
<tr>
<td>A check at start-up</td>
<td>The app sends the key and a machine id; the server answers valid or not, with a reason</td>
</tr>
<tr>
<td>Machine limits</td>
<td>One key can run on, say, 2 machines, so a shared key runs out of slots</td>
</tr>
<tr>
<td>Fail open</td>
<td>If the licensing server can't be reached, the app keeps working. Only a clear "no" stops it</td>
</tr>
<tr>
<td>A way to say no later</td>
<td>Suspend or revoke a key, and the next check refuses it</td>
</tr>
<tr>
<td>No secrets in the repo</td>
<td>The API key and test keys live in a gitignored <code>.env</code>, never in source or logs</td>
</tr>
</tbody></table>
<p>Agents are good at writing this code. They are less good at knowing which edge cases matter (fail open, masked keys, which token belongs in the app), which is why the prompt spells them out.</p>
<h2>Step 1: get the prompt</h2>
<p>There are two ways, depending on whether you have an account.</p>
<p><strong>Without an account:</strong> open <a href="https://sunlicense.com/prompt?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=guides-2026-11&amp;utm_content=ai-agent-license-keys">sunlicense.com/prompt</a> and press Copy. The agent creates a guest account for you, so you can try the whole thing before signing up.</p>
<p><strong>With an account:</strong> open <strong>Products → your product → Setup</strong> and choose <strong>Copy prompt</strong>. That version is tied to your account and that product, with the parts your plan includes switched on.</p>
<img src="https://raw.githubusercontent.com/sunlicense-com/guides/main/images/hashnode/01-step-copy-prompt.png" alt="The Setup tab of a product, with the Copy prompt button" style="display:block;margin:0 auto" />

<p>The prompt holds no keys. It's a set of instructions: what to build, the rules to follow, and what success looks like at each step.</p>
<h2>Step 2: paste it into your agent</h2>
<p>Open the project in Claude Code, Cursor, Codex, Copilot, Windsurf or Gemini CLI and paste the prompt. It's written to run without asking you questions: where it has to choose, it picks a sensible default and lists it at the end.</p>
<p>Here is what it does, in order:</p>
<ol>
<li><strong>Works out what the project is</strong>: language, what kind of product (desktop app, CLI, bot, web app, plugin), how it starts, where it keeps settings and how releases are built.</li>
<li><strong>Installs the SunLicense CLI</strong> and runs <code>sunlicense init</code>, which creates (or finds) a free account, a product, an API key and one test license, and writes <code>SUNLICENSE_API_KEY</code> and <code>SUNLICENSE_LICENSE_KEY</code> to a gitignored <code>.env</code>.</li>
<li><strong>Adds the license check</strong> with the official SDK for your language (Java, JavaScript, Python, .NET, PHP, Lua) or a plain HTTP call: once at start-up, stop the paid features on a rejection, keep working when SunLicense is unreachable.</li>
<li><strong>Adds protection</strong>: seals a small piece of the app (the "core unlock") so it only opens after a valid check, logs an ownership notice with the key masked, and reports modified copies.</li>
<li><strong>Adds anonymous product analytics</strong> (versions, OS, runtime), with a setting your users can switch off.</li>
</ol>
<img src="https://raw.githubusercontent.com/sunlicense-com/guides/main/images/hashnode/01-what-it-adds.png" alt="What the agent adds to your project" style="display:block;margin:0 auto" />

<p>The core unlock matters more for AI-built apps than it sounds. Anyone can open your app in their own editor and type "remove the license check". If a small part of the app is sealed and only a valid license opens it, deleting the check leaves a copy that doesn't run.</p>
<h2>Step 3: let it prove the check works</h2>
<p>Code that compiles isn't code that works. The prompt makes the agent test the check against the real service with the test license, in three moves:</p>
<ol>
<li>Run the app (or a short script that calls the check): it reports the license as <strong>VALID</strong>.</li>
<li><code>sunlicense licenses suspend test@example.com --yes</code>, run it again: rejected with <strong>SUSPENDED</strong>.</li>
<li><code>sunlicense licenses reinstate test@example.com</code>, run it again: <strong>VALID</strong>.</li>
</ol>
<img src="https://raw.githubusercontent.com/sunlicense-com/guides/main/images/hashnode/01-proof.png" alt="The agent tests the check: valid, suspended, valid again" style="display:block;margin:0 auto" />

<p>That sequence is the one that catches real bugs: a check that always says yes, one that caches the first answer forever, or one that treats a network error as a rejection. If a step gives the wrong answer, the agent fixes the code and repeats it.</p>
<p>It finishes with a short summary: which files changed, where the customer's key goes, how a release build gets the API key, and how to issue, suspend and reinstate licenses.</p>
<h2>Step 4: check these five things yourself</h2>
<p>The agent did the work; you own the result. Ask it to show you each of these, or read the diff:</p>
<ol>
<li><strong>The right key is in the app.</strong> Apps use an API key (<code>slk_...</code>), which can only validate licenses. A personal access token (<code>slp_...</code>) can manage your whole account and belongs to the CLI and your agent, never in a build you ship.</li>
<li><strong>No secrets in git.</strong> <code>git status</code> and <code>git diff</code> show no <code>.env</code>, no full license key and no API key. Logs print keys masked, like <code>AX8P-XXXX-XXXX-XXXX</code>.</li>
<li><strong>Unreachable is not a "no".</strong> Run the app once online, then turn off your Wi-Fi and start it again: it should keep working and log that it couldn't check (the sealed core stays open for 7 days after the last valid check). Only an answer like SUSPENDED or REVOKED should stop it.</li>
<li><strong>The built file works outside your project folder.</strong> Copy the release build somewhere else and run it with the test key. A build that only works because it finds your local <code>.env</code> is a build your buyers can't run.</li>
<li><strong>Your users can find where the key goes.</strong> A settings screen, a config file, an environment variable: whatever your kind of app uses, say so in the README and the purchase email.</li>
</ol>
<h2>Step 5: connect your store</h2>
<p>The last piece is issuing keys when people pay. Connect your store on the <strong>Integrations</strong> page: Stripe, Gumroad, Lemon Squeezy, Payhip, Ko-fi, <a href="http://itch.io">itch.io</a> and others. For Gumroad and Lemon Squeezy the key in the buyer's receipt is the key your app validates; for a Stripe Payment Link the buyer is sent to a page that shows their key. Refunds revoke the key automatically.</p>
<h2>What this doesn't do</h2>
<p>Licensing stops casual sharing: the forwarded download, the key posted in a Discord server, the copy running on ten machines. It doesn't make your app uncrackable. Someone with a valid license and enough time can still pull it apart. The sealed core and tamper reports make that slower and visible, not impossible. Set your expectations there and you'll be happy with the result.</p>
<h2>Try it</h2>
<p>The free plan covers 3 products and 100 licenses, with the SDKs, store integrations and the prompt included, and no credit card. If you're shipping something you built with an agent, the <a href="https://sunlicense.com/for/ai-built-apps?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=guides-2026-11&amp;utm_content=ai-agent-license-keys-end">SunLicense page for AI-built apps</a> shows the whole flow, and the docs for assistants are at <a href="https://docs.sunlicense.com/guide/ai-assistants">docs.sunlicense.com/guide/ai-assistants</a>.</p>
]]></content:encoded></item></channel></rss>