<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: PermitCore</title>
    <description>The latest articles on DEV Community by PermitCore (@permitcore).</description>
    <link>https://dev.to/permitcore</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4119717%2F8a675fea-fa4b-4688-b41b-64247900bc69.png</url>
      <title>DEV Community: PermitCore</title>
      <link>https://dev.to/permitcore</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/permitcore"/>
    <language>en</language>
    <item>
      <title>Perpetual License vs Subscription License: Which Should You Sell?</title>
      <dc:creator>PermitCore</dc:creator>
      <pubDate>Sun, 01 Nov 2026 09:27:06 +0000</pubDate>
      <link>https://dev.to/permitcore/perpetual-license-vs-subscription-license-which-should-you-sell-16gl</link>
      <guid>https://dev.to/permitcore/perpetual-license-vs-subscription-license-which-should-you-sell-16gl</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://permitcore.dev/blog/perpetual-vs-subscription-license" rel="noopener noreferrer"&gt;PermitCore blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Perpetual and subscription aren't really about the license technology at all. A license key doesn't care whether it's "supposed to" last forever. The real difference is a business model decision: does the customer own access forever after one payment, or do they keep paying to keep access? Most licensing platforms, including ours, can issue either from the exact same product.&lt;/p&gt;

&lt;h2&gt;
  
  
  How purchase type determines access
&lt;/h2&gt;

&lt;p&gt;For a one-time purchase in PermitCore Store, &lt;code&gt;LicenseExpiryDays&lt;/code&gt; sets the number of days of access; leaving it blank creates a perpetual key. Monthly and annual purchases follow Stripe's paid billing period instead: each paid invoice extends the same key. Failed renewals use the configured payment grace window, 14 days by default, and access expires when it ends.&lt;/p&gt;

&lt;p&gt;A product can offer one-time, monthly, and annual options side by side; the selected purchase type determines which expiry rule applies. Activations, node locking, and feature flags remain independent controls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Perpetual: what you're actually trading
&lt;/h2&gt;

&lt;p&gt;A perpetual license (one payment, access forever) is simple to explain to a customer and creates no ongoing billing relationship to maintain: no dunning emails, no cancellation flow, no "why was I charged again" support tickets.&lt;/p&gt;

&lt;p&gt;The tradeoff is revenue shape. Each sale is a one-time event, so growth depends entirely on new customers or paid upgrades, not a compounding base of renewals.&lt;/p&gt;

&lt;p&gt;It also creates a real, ongoing support obligation most vendors underestimate. A customer who paid once five years ago may still expect their software to run, even as the OS and platform change underneath them. Perpetual access does not itself promise endless feature upgrades or commercial support. Applicable security-support duties need separate assessment; they are not erased by an expired upgrade contract. Our &lt;a href="https://permitcore.dev/blog/cyber-resilience-act-software-developers" rel="noopener noreferrer"&gt;CRA support and licensing guide&lt;/a&gt; goes deeper. In short: state the purchased execution rights, the upgrade policy and the security-support period separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subscription: what you're actually trading
&lt;/h2&gt;

&lt;p&gt;A subscription (recurring charge, access requires staying current) creates a predictable, compounding revenue base and a natural way to fund ongoing development. The customer relationship doesn't end at the sale.&lt;/p&gt;

&lt;p&gt;The tradeoff is operational surface:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Failed payments need handling (grace window, then expiry).&lt;/li&gt;
&lt;li&gt;Cancellations need a clean flow.&lt;/li&gt;
&lt;li&gt;Every renewal is a moment a customer can decide to leave, so churn becomes a metric you actively manage.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Selling both isn't unusual
&lt;/h2&gt;

&lt;p&gt;A common pattern: subscription as the default (lower upfront price, steady revenue) with a perpetual option at a higher price point for customers who specifically want to avoid recurring billing. Enterprise procurement teams in particular often prefer a one-time purchase over an operating subscription for budgeting reasons that have nothing to do with your product.&lt;/p&gt;

&lt;p&gt;Since both are just a price plus an expiry setting on the same product, offering both isn't extra engineering work, just a second price point.&lt;/p&gt;

&lt;h2&gt;
  
  
  What doesn't change either way
&lt;/h2&gt;

&lt;p&gt;Node-locking, floating seats, feature gating, and offline support are all independent of whether the license is perpetual or subscription. The perpetual-vs-subscription decision is purely about the business model; how the license actually behaves on a customer's machine is a separate, orthogonal choice.&lt;/p&gt;




&lt;p&gt;Which model are you selling right now, and what made you pick it? I'd love to hear how other indie devs decided. 🙂&lt;/p&gt;

</description>
      <category>saas</category>
      <category>licensing</category>
      <category>indiehackers</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>How to Implement Feature-Based Licensing</title>
      <dc:creator>PermitCore</dc:creator>
      <pubDate>Fri, 30 Oct 2026 09:33:17 +0000</pubDate>
      <link>https://dev.to/permitcore/how-to-implement-feature-based-licensing-1f5d</link>
      <guid>https://dev.to/permitcore/how-to-implement-feature-based-licensing-1f5d</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://permitcore.dev/blog/feature-based-licensing" rel="noopener noreferrer"&gt;PermitCore blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;"Is this license valid?" is often the wrong question. The more useful one is usually "what does &lt;em&gt;this specific&lt;/em&gt; license unlock?" A Pro customer and a Team customer might both hold a genuinely valid key, but your app needs to know which of them gets the export button, SSO, or the API integration. That's feature-based licensing, and it's a field on the license key itself, not a separate system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where features live
&lt;/h2&gt;

&lt;p&gt;Every &lt;code&gt;LicenseKey&lt;/code&gt; has a &lt;code&gt;FeaturesJson&lt;/code&gt; field: a plain JSON array of feature-flag strings, e.g. &lt;code&gt;["export","api","sso"]&lt;/code&gt;. You set this when you create a key (or define it once on a License Template so every key generated from that template inherits the same list), and it's returned in full on every &lt;code&gt;validate()&lt;/code&gt;/&lt;code&gt;activate()&lt;/code&gt; call, no separate endpoint required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading it in your app
&lt;/h2&gt;

&lt;p&gt;The response's &lt;code&gt;features&lt;/code&gt; field is a plain string array. PermitCore's .NET SDK adds one convenience method on top of it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// Returns true if this feature flag is in the license's feature list.&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;HasFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;Features&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your own code, that's a one-line gate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ValidateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;licenseKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HasFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"export"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;exportButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Visible&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HasFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sso"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;EnableSsoLogin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The check is case-insensitive on purpose: a casing difference between how a feature was set on the key and how you check it in code is exactly the kind of bug that only shows up for one customer, weeks later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two ways to assign features
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Per-key.&lt;/strong&gt; Set &lt;code&gt;FeaturesJson&lt;/code&gt; directly when creating or editing an individual license. Useful for a one-off custom deal or an enterprise customer with a negotiated feature set that doesn't match any of your standard tiers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Via a License Template.&lt;/strong&gt; Define a template once (e.g. "Pro Template" with &lt;code&gt;["export","api"]&lt;/code&gt;, "Team Template" with &lt;code&gt;["export","api","sso","seats"]&lt;/code&gt;) and every key generated from it, whether created manually, through the Store, or via the Stripe checkout flow, inherits that feature list automatically. This is the right approach if you're selling more than one tier: define the tiers as templates once, and every future key just picks one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this fits versus other licensing controls
&lt;/h2&gt;

&lt;p&gt;Feature flags answer "what can this customer do," which is a different question from what &lt;a href="https://permitcore.dev/blog/node-locked-vs-floating-licenses" rel="noopener noreferrer"&gt;node-locked vs. floating&lt;/a&gt; answers ("how many devices or seats can use it") or what an expiry date answers ("for how long"). Most real products combine all three: a Pro-tier key might be node-locked to 2 devices, expire in 12 months, and unlock a specific feature set, all on the same license.&lt;/p&gt;

&lt;p&gt;How do you split features between tiers in your own product: flags on the license, or separate builds? 🙂&lt;/p&gt;

</description>
      <category>saas</category>
      <category>licensing</category>
      <category>gamedev</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>How to Prevent License Key Sharing (Three Layers That Work Together)</title>
      <dc:creator>PermitCore</dc:creator>
      <pubDate>Wed, 28 Oct 2026 09:31:54 +0000</pubDate>
      <link>https://dev.to/permitcore/how-to-prevent-license-key-sharing-three-layers-that-work-together-bca</link>
      <guid>https://dev.to/permitcore/how-to-prevent-license-key-sharing-three-layers-that-work-together-bca</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://permitcore.dev/blog/prevent-license-key-sharing" rel="noopener noreferrer"&gt;PermitCore blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A license key on its own is just a string. Nothing stops someone from pasting a valid one into a forum thread or a Discord server unless the system that checks the key actually does something about it. Here are three complementary techniques we use in PermitCore. Each one covers a different sharing pattern, and together they make casual key-sharing genuinely inconvenient.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Node-locking: tie a key to a device
&lt;/h2&gt;

&lt;p&gt;Every license has a &lt;strong&gt;Max Activations&lt;/strong&gt; limit: how many distinct devices it can be active on at once. With node-locking on, the first &lt;code&gt;activate()&lt;/code&gt; call binds the key to a hardware fingerprint (a hash derived from stable machine identifiers, nothing personally identifying). Reactivating on the same device doesn't use a new seat, but a genuinely different machine does, and once the limit is reached the next machine is rejected.&lt;/p&gt;

&lt;p&gt;That's the first line of defense: sharing a key with a friend costs one of your own seats the moment they use it, and once the limit is hit, both of you find out immediately. The &lt;a href="https://permitcore.dev/blog/device-activation-hwid" rel="noopener noreferrer"&gt;device activation guide&lt;/a&gt; covers how the fingerprinting works.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Floating licenses: shared seats, not shared copies
&lt;/h2&gt;

&lt;p&gt;For team products where node-locking doesn't fit, for example a license that should float to whoever on the team is using the app right now, floating (concurrent) licenses work better. A fixed number of sessions are checked out and released as people start and stop the app. When every seat is taken, the next activation waits until someone checks theirs back in.&lt;/p&gt;

&lt;p&gt;Simultaneous use stays bounded by what was paid for; it's just counted per concurrent session instead of per permanent device.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Abuse detection: the pattern seat limits can't see
&lt;/h2&gt;

&lt;p&gt;A key with a generous seat count (say a 50-seat team license) can still be posted publicly and used by far more than 50 people over time, as long as no more than 50 are active at once. Every individual request looks legitimate.&lt;/p&gt;

&lt;p&gt;So we watch for that pattern directly: per license key, how many distinct caller IPs hit &lt;code&gt;validate()&lt;/code&gt; / &lt;code&gt;activate()&lt;/code&gt; within a rolling one-hour window.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;RequestSuspiciousThreshold&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// requests/hour&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;RequestBlockThreshold&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// requests/hour&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;IpFanoutSuspiciousMultiplier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// × MaxActivations&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;IpFanoutSuspiciousFloor&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// distinct IPs/hour, minimum&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;IpFanoutBlockMultiplier&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// × MaxActivations&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;IpFanoutBlockFloor&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// distinct IPs/hour, minimum&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The threshold scales with the license's own seat count. A 1-seat license reaching 20 distinct IPs in an hour is already abnormal, while a 500-seat license needs proportionally more before it's flagged. There are two tiers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Suspicious:&lt;/strong&gt; the request is still served, so a real customer is never blocked over a false positive, but an audit entry is written and a &lt;code&gt;license.abuse_suspected&lt;/code&gt; webhook fires (at most once per key per hour) so you can decide what to do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block:&lt;/strong&gt; well past the suspicious threshold, the request is rejected outright before any database work, which protects your infrastructure from a key that's clearly being hammered from everywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why layering matters
&lt;/h2&gt;

&lt;p&gt;Node-locking and floating limits bound how much simultaneous use one key can support. Abuse detection catches the slower leak, where a key spreads more widely than its seat count alone would reveal. Combined with revocation, you can shut a leaked key down the moment the webhook tells you about it, without touching paying customers.&lt;/p&gt;

&lt;p&gt;How do you handle leaked keys in your own products today: hard limits, monitoring, or just revoking when someone reports it? 🙂&lt;/p&gt;

</description>
      <category>licensing</category>
      <category>saas</category>
      <category>gamedev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Node-Locked vs. Floating Licenses: Which One Do You Need?</title>
      <dc:creator>PermitCore</dc:creator>
      <pubDate>Mon, 26 Oct 2026 09:39:52 +0000</pubDate>
      <link>https://dev.to/permitcore/node-locked-vs-floating-licenses-which-one-do-you-need-iia</link>
      <guid>https://dev.to/permitcore/node-locked-vs-floating-licenses-which-one-do-you-need-iia</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://permitcore.dev/blog/node-locked-vs-floating-licenses" rel="noopener noreferrer"&gt;PermitCore blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Both options sit right next to each other on a typical Create License form, and it's not always obvious which one fits your product. The short version: node-locked ties a key to specific machines; floating ties a key to a &lt;em&gt;pool of concurrent seats&lt;/em&gt; that any machine can borrow from. Here's when each one actually makes sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Node-Locked: One Key, Specific Machines
&lt;/h2&gt;

&lt;p&gt;A node-locked license binds to a device's hardware fingerprint the first time it's activated, and stays bound until it's explicitly deactivated. &lt;code&gt;MaxActivations&lt;/code&gt; is a hard ceiling on how many distinct devices can ever hold that key at once.&lt;/p&gt;

&lt;p&gt;This is the right default for most desktop software: a customer buys one seat, installs it on their one work laptop, and that's that. It's simple to reason about, simple to support, and matches how most customers already think about "a license."&lt;/p&gt;

&lt;h2&gt;
  
  
  Floating: A Shared Pool of Seats
&lt;/h2&gt;

&lt;p&gt;A floating license doesn't permanently tie itself to any machine. Instead, your app &lt;strong&gt;checks out&lt;/strong&gt; a seat when it starts, and &lt;strong&gt;checks in&lt;/strong&gt; (releases it) when it closes, or after a heartbeat timeout if it crashes without checking in cleanly. &lt;code&gt;MaxActivations&lt;/code&gt; here means "how many machines can be actively using the software &lt;em&gt;right now&lt;/em&gt;," not "how many machines have ever run it."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs0758rpzwmnc3gpnx6ka.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs0758rpzwmnc3gpnx6ka.png" alt="Floating license configuration showing seat pool and heartbeat settings" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A 10-seat floating license lets any number of employees install the software, but only 10 can have it open at the same time.&lt;/p&gt;

&lt;p&gt;This is the right fit for office software used by a team that's never all logged in simultaneously: CAD tools, analytics dashboards, anything where you'd rather sell "10 concurrent users" than "10 named installs."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// On app start&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CheckoutFloatingSeatAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// result.ErrorCode == "SeatsExhausted" means every seat is in use right now&lt;/span&gt;
    &lt;span class="nf"&gt;ShowNoSeatsAvailable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Periodically, while the app is running&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HeartbeatAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SessionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// On clean app shutdown&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CheckinFloatingSeatAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SessionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Side by Side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Node-Locked&lt;/th&gt;
&lt;th&gt;Floating&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ties to&lt;/td&gt;
&lt;td&gt;a specific device&lt;/td&gt;
&lt;td&gt;a seat pool, any device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Seats" means&lt;/td&gt;
&lt;td&gt;total installs ever&lt;/td&gt;
&lt;td&gt;concurrent active users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Needs&lt;/td&gt;
&lt;td&gt;activate/deactivate&lt;/td&gt;
&lt;td&gt;checkout/heartbeat/checkin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;1 user, 1 machine&lt;/td&gt;
&lt;td&gt;shared office tools&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What Happens If a Floating Session Never Checks In?
&lt;/h2&gt;

&lt;p&gt;If a customer's machine crashes, loses power, or their app process is killed rather than closed cleanly, the seat isn't lost forever: a missed heartbeat past the configured timeout automatically frees the seat back into the pool for the next machine that needs it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Both models assume you're selling directly through your own app. The other distribution path is a built-in license store, for teams who want a real checkout page and automatic key delivery without building any of that themselves: &lt;a href="https://permitcore.dev/blog/sell-license-keys-store" rel="noopener noreferrer"&gt;Setting Up a License Store&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Which model do you use for your own desktop app, and did you ever have to switch? 🙂&lt;/p&gt;

</description>
      <category>saas</category>
      <category>licensing</category>
      <category>webdev</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>How to Build a License Key System</title>
      <dc:creator>PermitCore</dc:creator>
      <pubDate>Fri, 23 Oct 2026 08:28:35 +0000</pubDate>
      <link>https://dev.to/permitcore/how-to-build-a-license-key-system-922</link>
      <guid>https://dev.to/permitcore/how-to-build-a-license-key-system-922</guid>
      <description>&lt;p&gt;Whether you build one yourself or use a service, every license key system is answering the same three questions: what does a key look like, how do you check one without leaking what a valid one looks like, and how do you stop a valid key from working everywhere at once. Here's the real architecture, independent of any particular vendor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the key format
&lt;/h2&gt;

&lt;p&gt;A license key is a string a human might need to type, read over the phone, or paste from an email — that constrains the design more than it first seems. A sensible format groups characters for readability (&lt;code&gt;PERMIT-XXXX-XXXX-XXXX-XXXX&lt;/code&gt; rather than one long block), and a good charset avoids characters that are easy to confuse when hand-typed or read aloud — no &lt;code&gt;0&lt;/code&gt;/&lt;code&gt;O&lt;/code&gt;, no &lt;code&gt;1&lt;/code&gt;/&lt;code&gt;I&lt;/code&gt;/&lt;code&gt;l&lt;/code&gt;. A real example: &lt;code&gt;ABCDEFGHJKLMNPQRSTUVWXYZ23456789&lt;/code&gt; — deliberately missing the ambiguous characters, 32 symbols so each character carries 5 bits of entropy, and a 20-character key (the four &lt;code&gt;XXXX&lt;/code&gt; groups) gives 100 bits — comfortably beyond brute-force range.&lt;/p&gt;

&lt;h2&gt;
  
  
  Never store the plaintext key server-side
&lt;/h2&gt;

&lt;p&gt;This is the part naive implementations get wrong most often: storing the key as plaintext (or as a reversible encryption of it) means a stolen database dump hands an attacker every valid key directly. The correct approach is a one-way transform — hash the key, and look it up by hash, not by the key itself. A plain hash isn't quite enough either: a fast hash function (SHA-256 alone) lets an attacker with a stolen dump brute-force short/predictable keys offline at billions of guesses per second. The fix is a &lt;strong&gt;keyed&lt;/strong&gt; hash — HMAC with a secret that lives only on your server, never in the database — so even a full database theft is useless without also stealing the separate secret.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lookup path
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Customer sends the plaintext key in a POST body (never a URL — keeps it out of&lt;/span&gt;
&lt;span class="c1"&gt;// access logs, browser history, and CDN caches)&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;HMAC_SHA256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serverSecret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;SHA256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plaintextKey&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Look up by the keyed hash, never by the plaintext key itself&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LicenseKeys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;KeyHash&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you the property that matters most: your server can validate a key it has never seen the plaintext form of stored anywhere, and a database breach alone — without the separate HMAC secret — is not enough to forge or verify keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Activation limits need a real lock, not just a count check
&lt;/h2&gt;

&lt;p&gt;A naive "read the count, check it's under the limit, then increment" has a race window: two devices activating a nearly-full license within milliseconds of each other can both read the same under-limit count and both succeed, overselling a seat. The fix is an atomic conditional update (&lt;code&gt;UPDATE ... SET count = count + 1 WHERE count &amp;lt; max&lt;/code&gt;, checking the database's own row affected count) or a database-level lock scoped to that one license — tested against genuinely concurrent requests, not just sequential ones, since the race only shows up under real concurrency.&lt;/p&gt;

&lt;h2&gt;
  
  
  What tends to get skipped, and shouldn't
&lt;/h2&gt;

&lt;p&gt;A system that only handles the happy path is missing: per-key failed-lookup rate limiting (so a script can't enumerate keys by brute force), abuse detection for a valid key shared across too many distinct callers (a different problem than a rate limit catches — see &lt;a href="https://permitcore.dev/blog/prevent-license-key-sharing" rel="noopener noreferrer"&gt;how PermitCore detects this&lt;/a&gt;), and an offline story if your app needs to keep working without a network call (see &lt;a href="https://permitcore.dev/blog/why-mac-addresses-are-bad-hardware-ids" rel="noopener noreferrer"&gt;device fingerprinting&lt;/a&gt; and &lt;a href="https://permitcore.dev/blog/offline-license-validation-csharp" rel="noopener noreferrer"&gt;offline validation&lt;/a&gt;). None of these are exotic — they're just easy to underestimate until a real customer hits the gap.&lt;/p&gt;

&lt;p&gt;If building all of this yourself sounds like more than you want to maintain long-term, that's the actual tradeoff a licensing API like PermitCore is solving — see our honest &lt;a href="https://permitcore.dev/blog/build-vs-buy-software-licensing" rel="noopener noreferrer"&gt;build vs. buy breakdown&lt;/a&gt; for the fuller picture either way.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://permitcore.dev/blog/how-to-build-a-license-key-system" rel="noopener noreferrer"&gt;PermitCore blog&lt;/a&gt;. How do you handle license keys in your own apps? I'd love to hear what's worked for you.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>saas</category>
      <category>webdev</category>
      <category>indiehackers</category>
    </item>
    <item>
      <title>PermitCore Store 2.0: a branded software storefront built into licensing</title>
      <dc:creator>PermitCore</dc:creator>
      <pubDate>Mon, 12 Oct 2026 15:50:23 +0000</pubDate>
      <link>https://dev.to/permitcore/permitcore-store-20-a-branded-software-storefront-built-into-licensing-405e</link>
      <guid>https://dev.to/permitcore/permitcore-store-20-a-branded-software-storefront-built-into-licensing-405e</guid>
      <description>&lt;p&gt;A licensing API solves key creation and validation. It does not automatically give a software vendor a polished way to present products, run promotions, take payment, deliver licenses, and support customers after checkout.&lt;/p&gt;

&lt;p&gt;That is why we rebuilt the PermitCore Store.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Store 2.0 is a responsive, vendor-branded software storefront connected directly to the same products, prices, entitlements, orders, subscriptions, and licenses managed in PermitCore.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg0u2cjnydd9bsqx4qlq1.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg0u2cjnydd9bsqx4qlq1.webp" alt="PermitCore Store 2.0 branded storefront" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Merchandising without building another application
&lt;/h2&gt;

&lt;p&gt;Vendors can now create a storefront with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hero campaigns and featured releases&lt;/li&gt;
&lt;li&gt;curated product collections&lt;/li&gt;
&lt;li&gt;category and workflow tiles&lt;/li&gt;
&lt;li&gt;horizontal product shelves&lt;/li&gt;
&lt;li&gt;custom colors, backgrounds, images, spacing, and labels&lt;/li&gt;
&lt;li&gt;search and responsive desktop/mobile navigation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every section can be configured from the Shop Designer instead of being hard-coded into a separate storefront project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fen4rr4ziu9dcwcrnx4cy.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fen4rr4ziu9dcwcrnx4cy.webp" alt="Store 2.0 visual merchandising" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Trials, bundles, and scheduled discounts
&lt;/h2&gt;

&lt;p&gt;Store 2.0 supports one-time and subscription products, free trials, bundles, scheduled promotions, percentage or fixed-value discounts, and campaign labels.&lt;/p&gt;

&lt;p&gt;For discounted offers, vendors can also show the lowest applied price from the previous 30 days. This gives EU-facing stores the information needed for transparent discount presentation while keeping the display configurable for stores operating elsewhere.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvn8kcgt3lyr1owl9a4m9.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvn8kcgt3lyr1owl9a4m9.webp" alt="Store 2.0 product page" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Checkout and licensing stay connected
&lt;/h2&gt;

&lt;p&gt;A buyer can discover a product, add it to the cart, pay through the vendor's connected Stripe account, and receive the correct license automatically.&lt;/p&gt;

&lt;p&gt;The complete workflow is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a product and define its license rules.&lt;/li&gt;
&lt;li&gt;Implement validation through the REST API or one of eight official SDKs.&lt;/li&gt;
&lt;li&gt;Merchandise the product in a branded Store 2.0 storefront.&lt;/li&gt;
&lt;li&gt;Accept payment through the vendor's own Stripe account.&lt;/li&gt;
&lt;li&gt;Generate and deliver the correct license automatically.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;PermitCore takes &lt;strong&gt;0% commission on software sales&lt;/strong&gt;. The vendor keeps the Stripe relationship while PermitCore connects checkout to customers, orders, invoices, subscriptions, entitlements, and license delivery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built for desktop and mobile
&lt;/h2&gt;

&lt;p&gt;The storefront, product pages, cart, checkout flow, and customer account are responsive. Storefront content is cached for fast delivery, while checkout, account, and license operations remain protected by the platform's authorization, rate-limiting, and tenant-isolation controls.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F48dsjhq2qxsla7o9452f.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F48dsjhq2qxsla7o9452f.webp" alt="Store 2.0 on mobile" width="430" height="932"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the presentation store
&lt;/h2&gt;

&lt;p&gt;We turned Nexova into a complete demo storefront so the entire flow can be explored with realistic products, imagery, bundles, trials, and discounts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://permitcore.dev/store/nexova" rel="noopener noreferrer"&gt;Open the live Nexova demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://permitcore.dev/solutions/sell-license-keys" rel="noopener noreferrer"&gt;Explore every Store 2.0 capability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://permitcore.dev/docs/store" rel="noopener noreferrer"&gt;Read the Shop Designer tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://permitcore.dev/blog/permitcore-store-2-0" rel="noopener noreferrer"&gt;Read the full announcement&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How are you currently connecting product discovery, checkout, and license delivery?&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>saas</category>
      <category>webdev</category>
      <category>marketing</category>
    </item>
    <item>
      <title>Offline licensing for a Python desktop app without calling home on every launch</title>
      <dc:creator>PermitCore</dc:creator>
      <pubDate>Tue, 29 Sep 2026 18:48:19 +0000</pubDate>
      <link>https://dev.to/permitcore/offline-licensing-for-a-python-desktop-app-without-calling-home-on-every-launch-292</link>
      <guid>https://dev.to/permitcore/offline-licensing-for-a-python-desktop-app-without-calling-home-on-every-launch-292</guid>
      <description>&lt;p&gt;Some desktop applications cannot depend on an internet connection every time they start. The useful security model in that case is not to hide a shared secret inside the Python package. It is to verify a signed license token locally with a public key.&lt;/p&gt;

&lt;p&gt;The server keeps the private signing key. The application receives only the public verification key, so it can verify authenticity without gaining the ability to create new valid licenses.&lt;/p&gt;

&lt;p&gt;I wrote a practical PermitCore guide for this flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;permitcore[offline]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the first run, verify the signed token and bind it to the device:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;permitcore&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PermitCoreClient&lt;/span&gt;

&lt;span class="n"&gt;activated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PermitCoreClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;activate_offline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;public_key_base64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;activated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_valid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;activated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Later launches can validate the stored activation locally, with zero network calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PermitCoreClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate_offline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;device_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A good test is to change one character in the activation token and confirm that signature verification fails. I would also test expired tokens, the wrong device, a missing local cache, and the customer workflow for moving to replacement hardware.&lt;/p&gt;

&lt;p&gt;For device identity, avoid treating a raw MAC address as a permanent machine ID. It can change, it is privacy-sensitive, and virtual/network adapters make it unreliable. Use a stable identifier appropriate for the platform and define an explicit transfer policy.&lt;/p&gt;

&lt;p&gt;The same product can then be published in PermitCore's built-in Store. The Store uses your Stripe account, takes 0% PermitCore commission, and delivers the license after checkout. That completes the full &lt;strong&gt;create → implement → sell&lt;/strong&gt; path even for an offline-ready desktop edition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full walkthrough
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://permitcore.dev/tutorials/offline-licensing-python" rel="noopener noreferrer"&gt;How to add offline licensing to a Python desktop app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://permitcore.dev" rel="noopener noreferrer"&gt;PermitCore&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PermitCore is an early-stage developer-first startup, so feedback on the offline workflow and the guide is very welcome. 🙂🚀&lt;/p&gt;

</description>
      <category>python</category>
      <category>security</category>
      <category>tutorial</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Where license validation belongs in an Electron app</title>
      <dc:creator>PermitCore</dc:creator>
      <pubDate>Tue, 29 Sep 2026 18:47:25 +0000</pubDate>
      <link>https://dev.to/permitcore/where-license-validation-belongs-in-an-electron-app-2on8</link>
      <guid>https://dev.to/permitcore/where-license-validation-belongs-in-an-electron-app-2on8</guid>
      <description>&lt;p&gt;If an Electron app checks its license in renderer code, a customer can inspect the same code that makes the allow-or-block decision. That does not make the app impossible to protect, but it puts an important decision in the easiest process to inspect and modify.&lt;/p&gt;

&lt;p&gt;For a paid Electron app, I prefer this boundary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validate in the main process;&lt;/li&gt;
&lt;li&gt;keep &lt;code&gt;contextIsolation&lt;/code&gt; enabled;&lt;/li&gt;
&lt;li&gt;expose only the minimum license status through a narrow preload/IPC API;&lt;/li&gt;
&lt;li&gt;scope every validation request to the product ID;&lt;/li&gt;
&lt;li&gt;test the packaged build, not only development mode.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core integration can remain straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;electron&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PermitCoreClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@permitcore/permitcore&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PermitCoreClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.permitcore.dev&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;licenseKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getVersion&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nx"&gt;productId&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wrote a complete walkthrough covering installation, activation, product-scoped validation, offline grace, revoked-key testing, and the main-process/renderer boundary.&lt;/p&gt;

&lt;p&gt;It also covers the step that licensing tutorials often omit: selling the application. PermitCore includes a branded Store, so the same product can have a checkout that uses your Stripe account, creates the license after payment, and delivers it to the customer. PermitCore takes 0% sales commission.&lt;/p&gt;

&lt;p&gt;The intended workflow is &lt;strong&gt;create → implement → sell&lt;/strong&gt;, without maintaining a separate storefront and webhook bridge just to issue keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full walkthrough
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://permitcore.dev/tutorials/license-sell-electron-app" rel="noopener noreferrer"&gt;How to license and sell an Electron app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://permitcore.dev" rel="noopener noreferrer"&gt;PermitCore&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you ship Electron applications, I would be interested to hear how you currently separate licensing logic from renderer code and how you handle offline customers. 🙂🚀&lt;/p&gt;

</description>
      <category>electron</category>
      <category>javascript</category>
      <category>security</category>
      <category>node</category>
    </item>
    <item>
      <title>A practical license-key flow for a .NET desktop app, including the sale</title>
      <dc:creator>PermitCore</dc:creator>
      <pubDate>Tue, 29 Sep 2026 18:41:28 +0000</pubDate>
      <link>https://dev.to/permitcore/a-practical-license-key-flow-for-a-net-desktop-app-including-the-sale-a1j</link>
      <guid>https://dev.to/permitcore/a-practical-license-key-flow-for-a-net-desktop-app-including-the-sale-a1j</guid>
      <description>&lt;p&gt;Adding a license check to a .NET desktop app is easy to underestimate. The first API call is usually the simple part. Production behavior is where the real work begins: product scoping, device activation, offline grace, license transfer, failed payments, and getting the key into the customer's hands.&lt;/p&gt;

&lt;p&gt;I put together a .NET tutorial that treats licensing as a complete product flow rather than one &lt;code&gt;IsValid&lt;/code&gt; check.&lt;/p&gt;

&lt;p&gt;Install the client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package PermitCore.Client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then validate the key against the application version and the exact product:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;PermitCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Reflection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PermitCoreClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.permitcore.dev"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Assembly&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEntryAssembly&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nf"&gt;GetName&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ValidateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;licenseKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;expectedProductId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;ShowActivationWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before shipping, I would test at least these cases in the packaged application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the correct license and product;&lt;/li&gt;
&lt;li&gt;a valid license for a different product;&lt;/li&gt;
&lt;li&gt;an expired or revoked license;&lt;/li&gt;
&lt;li&gt;a device over its activation limit;&lt;/li&gt;
&lt;li&gt;a temporary network failure while offline grace is available;&lt;/li&gt;
&lt;li&gt;releasing an activation and moving the license to another computer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tutorial also finishes the commercial side. You can create a matching Store product, use one-time or recurring Stripe pricing, and have checkout generate and email the license. PermitCore uses the vendor's Stripe account and takes 0% PermitCore commission.&lt;/p&gt;

&lt;p&gt;That is the distinction I wanted when building it: developers can &lt;strong&gt;create the license, implement it, and sell the software&lt;/strong&gt; without maintaining a separate checkout-to-license webhook system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full walkthrough
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://permitcore.dev/tutorials/add-license-keys-dotnet" rel="noopener noreferrer"&gt;How to add license keys to a .NET application&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://permitcore.dev" rel="noopener noreferrer"&gt;PermitCore&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is still an early-stage startup and I would especially appreciate feedback from people who ship WPF, WinUI, Avalonia, MAUI, or other desktop applications. 🙂🚀&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>security</category>
      <category>showdev</category>
    </item>
    <item>
      <title>How I added licensing and checkout to a Unity project without building two more systems</title>
      <dc:creator>PermitCore</dc:creator>
      <pubDate>Tue, 29 Sep 2026 18:29:54 +0000</pubDate>
      <link>https://dev.to/permitcore/how-i-added-licensing-and-checkout-to-a-unity-project-without-building-two-more-systems-1ak9</link>
      <guid>https://dev.to/permitcore/how-i-added-licensing-and-checkout-to-a-unity-project-without-building-two-more-systems-1ak9</guid>
      <description>&lt;p&gt;When I started working on software licensing, the part that bothered me was not only validating a key. A paid Unity game or asset needs the whole path: a product, a license policy, SDK integration, payment, and finally a way to deliver the right key to the customer.&lt;/p&gt;

&lt;p&gt;Most licensing services stop after validation. You still need to connect a separate storefront, handle payment webhooks, generate the license, and send it to the buyer. That can become a second product to maintain before the actual game or tool has made its first sale.&lt;/p&gt;

&lt;p&gt;I wrote a practical Unity guide showing the complete flow with PermitCore:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a product and choose device, version, and offline rules.&lt;/li&gt;
&lt;li&gt;Install the Unity SDK through Package Manager.&lt;/li&gt;
&lt;li&gt;Validate the license against the expected product ID.&lt;/li&gt;
&lt;li&gt;Test revoked, expired, wrong-product, and activation-limit responses.&lt;/li&gt;
&lt;li&gt;Publish the same product in a branded Store, connect your Stripe account, and let checkout issue and deliver the license automatically.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The integration itself stays small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;PermitCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;UnityEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PermitCoreClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.permitcore.dev"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ValidateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;licenseKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;ShowLicenseScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important detail is &lt;code&gt;productId&lt;/code&gt;. A structurally valid key should not unlock the wrong game, asset, edition, or DLC. I also recommend testing a revoked key in the actual release build rather than stopping after the happy path works in the editor.&lt;/p&gt;

&lt;p&gt;PermitCore's Store uses your own Stripe account and takes 0% PermitCore sales commission. The goal is to keep licensing and selling in one workflow: &lt;strong&gt;create → implement → sell&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full walkthrough
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://permitcore.dev/tutorials/sell-unity-game-asset-licenses" rel="noopener noreferrer"&gt;How to sell licenses for a Unity game or asset&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://permitcore.dev" rel="noopener noreferrer"&gt;PermitCore&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am building this as a small developer-first startup, so feedback from Unity developers is very welcome. If something in the guide is unclear, you can reach me through Contact Us on the site. 🙂🚀&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>csharp</category>
      <category>tutorial</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Why a licensing API is only half of the product</title>
      <dc:creator>PermitCore</dc:creator>
      <pubDate>Mon, 21 Sep 2026 18:13:51 +0000</pubDate>
      <link>https://dev.to/permitcore/why-a-licensing-api-is-only-half-of-the-product-2nme</link>
      <guid>https://dev.to/permitcore/why-a-licensing-api-is-only-half-of-the-product-2nme</guid>
      <description>&lt;p&gt;When we first looked at software licensing, the problem seemed straightforward: create a license key, validate it inside the application, and manage activations.&lt;/p&gt;

&lt;p&gt;Then we reached the part where somebody actually has to buy the license.&lt;/p&gt;

&lt;p&gt;That turns licensing into a much larger project. You need a product page, pricing, checkout, payment verification, customers, orders, subscriptions, license fulfillment, delivery, and somewhere for the customer to find what they purchased. The license API may be ready, but the commercial workflow is still another product to build and maintain.&lt;/p&gt;

&lt;p&gt;That is the gap we are trying to close with PermitCore.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create → implement → sell
&lt;/h2&gt;

&lt;p&gt;PermitCore connects the full workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a product and define its license rules.&lt;/li&gt;
&lt;li&gt;Add validation with the REST API or one of eight official SDKs: .NET, Node.js, Python, Java, C++, PHP, Go, and Unity.&lt;/li&gt;
&lt;li&gt;Publish the product in a customer-facing Store under your own brand.&lt;/li&gt;
&lt;li&gt;Accept payment through your connected Stripe account.&lt;/li&gt;
&lt;li&gt;Generate and deliver the working license automatically after checkout.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Products, prices, orders, customers, subscriptions, entitlements, and licenses stay connected instead of being split across a licensing dashboard, a custom checkout service, and several webhook handlers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we made the Store part of licensing
&lt;/h2&gt;

&lt;p&gt;Many licensing platforms treat commerce as an integration boundary.&lt;/p&gt;

&lt;p&gt;For example, Keygen documents a Stripe flow where Stripe communicates with the licensing service through webhooks. LicenseSpring connects license fulfillment to services such as Stripe, FastSpring, and Shopify. Those are valid approaches, especially if a team already has a mature commerce stack.&lt;/p&gt;

&lt;p&gt;But for an indie developer or a small software team, the missing checkout and fulfillment layer can become a second application. It has its own security work, failure states, support burden, and maintenance cost.&lt;/p&gt;

&lt;p&gt;PermitCore takes a different approach: the Store is part of the licensing product. A vendor can go from defining a license to publishing the product for sale without building a separate commerce backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your Stripe account, 0% PermitCore commission
&lt;/h2&gt;

&lt;p&gt;Payments go directly through the vendor's connected Stripe account. PermitCore does not take a percentage of software sales.&lt;/p&gt;

&lt;p&gt;That means the Store removes integration work without becoming a marketplace that sits between the vendor and the customer. Stripe still handles the payment relationship, while PermitCore connects a successful checkout to the correct order, entitlement, and license delivery.&lt;/p&gt;

&lt;h2&gt;
  
  
  When a separate commerce platform still makes sense
&lt;/h2&gt;

&lt;p&gt;The built-in Store will not be the right answer for every business. If you already run a large Shopify, WooCommerce, or custom commerce operation, keeping that system and connecting fulfillment may make more sense.&lt;/p&gt;

&lt;p&gt;Our goal is to give developers a complete default path when they do not want to build that entire layer themselves.&lt;/p&gt;

&lt;p&gt;We are still a startup and continuing to produce documentation and videos, so our social channels are smaller than the product today. If you need anything while evaluating PermitCore, use the Contact Us page and we will help directly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://permitcore.dev" rel="noopener noreferrer"&gt;https://permitcore.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Store overview: &lt;a href="https://permitcore.dev/solutions/sell-license-keys" rel="noopener noreferrer"&gt;https://permitcore.dev/solutions/sell-license-keys&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Product video: &lt;a href="https://www.youtube.com/watch?v=148yf66x9IM" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=148yf66x9IM&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;YouTube: &lt;a href="https://www.youtube.com/@PermitCore" rel="noopener noreferrer"&gt;https://www.youtube.com/@PermitCore&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;X: &lt;a href="https://x.com/permitcore" rel="noopener noreferrer"&gt;https://x.com/permitcore&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How are you handling this in your own products: a custom checkout, an external commerce platform, or one connected licensing and sales workflow?&lt;/p&gt;

&lt;p&gt;🙂🚀&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>saas</category>
      <category>webdev</category>
      <category>security</category>
    </item>
    <item>
      <title>Why we built PermitCore: software licensing shouldn’t become a second product</title>
      <dc:creator>PermitCore</dc:creator>
      <pubDate>Thu, 10 Sep 2026 17:41:53 +0000</pubDate>
      <link>https://dev.to/permitcore/why-we-built-permitcore-software-licensing-shouldnt-become-a-second-product-21p6</link>
      <guid>https://dev.to/permitcore/why-we-built-permitcore-software-licensing-shouldnt-become-a-second-product-21p6</guid>
      <description>&lt;p&gt;Hey DEV community,&lt;/p&gt;

&lt;p&gt;I want to introduce &lt;strong&gt;PermitCore&lt;/strong&gt;, a software licensing platform we built for independent developers and software teams.&lt;/p&gt;

&lt;p&gt;The idea came from a problem we kept running into ourselves. Whenever we built software that needed licensing, we also had to build and maintain a separate system for license keys, device activations, expiration rules and customer access.&lt;/p&gt;

&lt;p&gt;That infrastructure quickly started feeling like another product we had to maintain.&lt;/p&gt;

&lt;p&gt;So we built PermitCore to handle it.&lt;/p&gt;

&lt;p&gt;PermitCore currently supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;License key creation and management&lt;/li&gt;
&lt;li&gt;Device activation limits&lt;/li&gt;
&lt;li&gt;Expiration and revocation&lt;/li&gt;
&lt;li&gt;Offline license validation&lt;/li&gt;
&lt;li&gt;Floating licenses and concurrent seats&lt;/li&gt;
&lt;li&gt;Entitlements and feature access&lt;/li&gt;
&lt;li&gt;Customer and reseller portals&lt;/li&gt;
&lt;li&gt;Payments and webhooks&lt;/li&gt;
&lt;li&gt;Zero-knowledge encryption&lt;/li&gt;
&lt;li&gt;A REST API and SDKs for .NET, Node.js, Python, Java, C++, PHP, Go and Unity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is a &lt;strong&gt;free plan that stays free&lt;/strong&gt;, because we want indie developers to have something useful while getting their first products off the ground. We also offer a &lt;strong&gt;three-month free trial&lt;/strong&gt; for anyone who wants to test the wider feature set without commitment.&lt;/p&gt;

&lt;p&gt;PermitCore is still an early startup, and we’re continuing to improve both the product and our documentation. Our social channels are also fairly new, so I hope you won’t mind that there isn’t a huge amount of content there yet.&lt;/p&gt;

&lt;p&gt;I’d really appreciate feedback from developers who have built licensing systems before or are currently deciding how to protect and sell their software.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://permitcore.dev" rel="noopener noreferrer"&gt;https://permitcore.dev&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Documentation:&lt;/strong&gt; &lt;a href="https://permitcore.dev/docs/getting-started" rel="noopener noreferrer"&gt;https://permitcore.dev/docs/getting-started&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;YouTube:&lt;/strong&gt; &lt;a href="https://www.youtube.com/@PermitCore" rel="noopener noreferrer"&gt;https://www.youtube.com/@PermitCore&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;X:&lt;/strong&gt; &lt;a href="https://x.com/permitcore" rel="noopener noreferrer"&gt;https://x.com/permitcore&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;If you have any questions or need help integrating it, feel free to reach us through the &lt;strong&gt;Contact Us&lt;/strong&gt; page on the website.&lt;/p&gt;

&lt;p&gt;Thanks for taking a look 🙂 👋&lt;/p&gt;

</description>
      <category>saas</category>
      <category>showdev</category>
      <category>security</category>
      <category>api</category>
    </item>
  </channel>
</rss>
