Skip to content

Licence keys

A licence key is the signed text a customer types into your app. It carries the licence’s terms, so your app can check it without a network connection; the API is only asked about the machine. Here is a key for a made-up app, Acme Notes. It is a real signature, made with a published test key, so you can try your verifier on it:

ACN1-ATyB4KJbfU8Zmm4tjEsfejAAAAAAarxRAAAAAAAAAAAAAQAAAAMAAQphY21lLW5vdGVzAA9hZGFAZXhhbXBsZS5jb20Gpxrgsiohif_Q7qFCDoKXxCOtUYTz0Kjyl9MBSPk54_lgpjh2UUfhKC5-9PWQlHIVWAUkmI6RwQb7WW6jmuMP

To check keys in your app, read Verify a key offline. This page is the format behind it.

A key is the product’s prefix, then a base64url text (no padding) of the payload followed by its 64-byte signature:

key = prefix || base64url_nopad( payload || signature )
signature = Ed25519( licence_domain || 0x00 || payload )

The payload is a fixed layout with big-endian integers, so one licence has exactly one key:

Bytes Field Rules
1 Format version 1
16 Licence id The licence’s UUID, as bytes
8 Issued at, Unix seconds
8 Expires at, Unix seconds 0 is perpetual; otherwise after issued
1 Limit kind 0 seats, 1 machines
4 Limit count 1 to 4294967295
2 Major version the licence was sold for 0 to 65535
1 + n Product id: a length byte, then UTF-8 1 to 255 bytes
2 + n Licensee email: 2-byte length, then UTF-8 1 to 320 bytes

Whitespace anywhere in a key is ignored, so a key pasted from an email with line breaks still works. Decoders accept only what the encoder produces: a key with trailing bytes, an unknown limit kind, a zero limit, an expiry that is not after the issue time or padding bits set in its base64url text is malformed.

The Acme Notes key above is for the licence 3c81e0a2-5b7d-4f19-9a6e-2d8c4b1f7a30, issued to ada@example.com on 30 Sep 2026 (1790726400), perpetual, for 3 machines, product acme-notes, major version 1. Its payload is 68 bytes:

01 format version 1
3c81e0a25b7d4f199a6e2d8c4b1f7a30 licence id
000000006abc5100 issued at 1790726400
0000000000000000 expires at 0 (perpetual)
01 limit kind: machines
00000003 limit count 3
0001 major 1
0a 61636d652d6e6f746573 product id, 10 bytes: "acme-notes"
000f 616461406578616d706c652e636f6d email, 15 bytes: "ada@example.com"

The signed message is acme-notes/licence/v1, a zero byte, then those 68 bytes. The signature comes from the test seed of 32 bytes of 0x07, whose public key is:

ea4a6c63e29c520abef5507b132ec5f9954776aebebe7b92421eea691446d22c

Never use that seed or key for a real product: it is published here. A real product’s key is made by Sealcord, and its private half is never shown.

Each product has its own Ed25519 signing key (RFC 8032). Sealcord generates it, keeps it encrypted and never shows it; your app embeds the public half, which the Console shows on the product’s page.

The signature covers a domain string for the product, a zero byte, then the payload. A product’s licence domain is <product id>/licence/v1 and its verdict domain <product id>/verdict/v1, unless you chose others when you added the product. The two differ, so a signature made for a key can never pass as a verdict, and the other way round.

The seat or machine limit is carried in the key, but a key alone cannot enforce it: only the API knows how many machines have activated. A key also does not say whether the licence was revoked since it was issued. Both come from activating the machine, and from the signed verdict your app keeps for when it is offline.

A build of your app embeds one public key, and a build you already shipped cannot change it. Plan a rotation with your releases: rotate a signing key says how, and how to retire the previous key.

Can a customer edit a key to get more machines?

Section titled “Can a customer edit a key to get more machines?”

No. Any change to the payload makes the signature fail, and your app and the API both refuse the key. The limit that counts is the one the API holds for the licence.

Does the key contain the customer’s email?

Section titled “Does the key contain the customer’s email?”

Yes: the licensee’s address is in the payload, so your app can show who a licence belongs to. Anyone who holds the key can read it, so treat a key as the customer’s secret and do not log it.

Why does the key change when I rotate the signing key?

Section titled “Why does the key change when I rotate the signing key?”

The signature is part of the key. After a rotation, fetching the licence from the Admin API returns its key under the new signing key; the keys already sold keep verifying with the old public key, if you kept it, until you retire it.

No. Only Sealcord holds a product’s private key, so only Sealcord issues keys. Your app, and any tool of yours, can verify them.