惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The GitHub Blog
The GitHub Blog
阮一峰的网络日志
阮一峰的网络日志
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Apple Machine Learning Research
Apple Machine Learning Research
小众软件
小众软件
博客园 - 司徒正美
Last Week in AI
Last Week in AI
爱范儿
爱范儿
罗磊的独立博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园_首页
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
雷峰网
雷峰网
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
WordPress大学
WordPress大学
Jina AI
Jina AI
人人都是产品经理
人人都是产品经理
量子位
V
V2EX
博客园 - 叶小钗
宝玉的分享
宝玉的分享
T
Tailwind CSS Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Licensing a Cross-Platform App from One Control Plane
Nico · 2026-06-27 · via DEV Community

Nico

Originally published on the Keylight blog.

If you ship the same product on more than one platform — a native Mac build, an Electron or Tauri desktop app, a Rust CLI — the licensing question is not "how do I license each one?" It is "how do I license all of them without running three disconnected systems?" The answer is a single control plane: one tenant that owns your customers, keys, and device limits, with thin SDKs in each app that all read the same signed lease. This post is the build guide for that setup.

What "one control plane" means

A control plane is the place the licensing truth lives. With Keylight it is your tenant: one account that holds every customer record, every license key, the activation count and limit on each key, the entitlements each key grants, and the Stripe connection that mints keys on purchase. The dashboard, the revocation controls, the activation resets — all of it operates on that one set of records.

The SDKs are deliberately not the control plane. They are clients. Each one does the same three things in its own language: exchange a key for a signed lease, verify that lease locally, and read entitlements from it. None of them holds authority over whether a license is valid — they verify a document the control plane signed. That separation is the whole point. It means a customer who buys once exists once, and a device limit of three means three devices total, not three per platform.

This is the universal licensing layer idea made concrete: licensing is infrastructure you configure once, not a backend you rebuild per app.

One lease format, every SDK

What makes a single control plane work across languages is that the lease format is shared. A Keylight lease is an Ed25519-signed document with a fixed wire format. Signing happens once, server-side; every SDK reconstructs and verifies that same format against the tenant's public keys. The Swift SDK and the Rust crate are not two implementations of "a license" — they are two readers of one signed artifact.

The practical consequence: the SDKs are at parity by construction. The same license states exist everywhere — Licensed, Trial, Limited, FreeTier, Expired, Invalid — because they are derived from the same lease fields. hasEntitlement("pro") means the identical thing in Swift, JavaScript, and Rust, because all three are checking the same signed entitlements field. For the field-by-field anatomy of that shared document, see what is inside a Keylight lease.

Wiring every SDK to one tenant

Here is the same product, configured against one tenant in each of Keylight's SDKs. Notice that the tenantId and productId are identical in every one — that is what binds them to the same control plane.

The native Mac build, in Swift, via the Keylight.manager factory:

let licensing = try Keylight.manager(
    sdkKey: "sdk_live_…",
    tenantId: "your-tenant",
    productId: "your-product",
    keyPrefix: "PROD",
    trustedPublicKeyBase64: "<tenant public key>",
    trialDurationDays: 14,
    branding: .init(
        appName: "My App",
        purchaseURL: URL(string: "https://example.com/buy")!,
        supportEmail: "support@example.com",
        tintColor: .accentColor
    )
)

A Tauri desktop build, registered in the Rust core:

use keylight::KeylightConfig;

let cfg = KeylightConfig::builder("your-tenant", "your-product", "sdk_live_…").build();
tauri::Builder::default()
    .plugin(tauri_plugin_keylight::init(cfg));

A Rust CLI or a non-Tauri Rust app, using the crate directly:

use keylight::{Keylight, KeylightConfig};

let cfg = KeylightConfig::builder("your-tenant", "your-product", "sdk_live_…").build();
let kl = Keylight::new(cfg)?;
let res = kl.activate("USER-LICENSE-KEY")?;

And an Electron build, with the JavaScript SDK in the main process:

const kl = new Keylight({
  tenantId: "your-tenant",
  productId: "your-product",
});

Four apps, one tenant. A customer's key activates on their Mac app and their CLI, and both activations count against the same limit. Revoke that key once in the dashboard and every app rejects it on the next revalidation. There are per-platform integration walkthroughs in how to add license keys to a Swift macOS app, an Electron app, and a Tauri app.

Where this lands today, honestly

The control plane is real and shared, but it is worth being precise about what "cross-platform" covers right now rather than overselling it. The shipped SDKs are Swift (Apple platforms), JavaScript/TypeScript (web, Node, Electron, edge), Rust, and the first-party Tauri plugin. Those cover the bulk of where independent desktop apps ship today.

What this is not, yet, is a drop-in REST API you can call from any language that lacks a native SDK. Additional language SDKs are added based on real demand, not to fill a matrix. The design makes that cheap: because the lease format and the control plane are shared, a new SDK is another reader of the same document, not a new system. So the honest framing is "one control plane across the SDKs that exist" — which is most of the market — rather than "licenses any language anywhere." When you only ship on platforms with a Keylight SDK, the single-tenant model already holds end to end.


A cross-platform app does not need cross-platform licensing complexity. It needs one place the licensing truth lives and thin, identical clients in each app reading the same signed lease. Configure the tenant once, point each SDK at it, and the platform count stops being a licensing problem. If you are wiring up a stack this post does not cover, send us your feedback and we'll help.

FAQ

Can one license system cover apps written in different languages?

Yes. Keylight issues the same Ed25519-signed lease format regardless of which SDK reads it. The Swift, JavaScript, Rust, and Tauri SDKs all verify that one format, so a single tenant covers a Mac app, an Electron app, and a Rust CLI at once.

Do customers and device limits carry across platforms?

Yes — they live on the tenant, not in the SDK. A customer, their key, its activation count, and its entitlements are one record. Whichever SDK activates a device increments the same count against the same limit.

Which platforms does Keylight cover today?

Shipped SDKs are Swift (Apple), JavaScript/TypeScript (web, Node, Electron), Rust, and a first-party Tauri plugin. More language SDKs are added based on demand; the lease format and control plane are shared, so new SDKs slot into the same tenant.