CoreInto

Licensing Manager

The Licensing Manager sells software by subscription. A product that uses it gives every service a licence key instead of an account on a server.

CoreInto administrator guide: Licensing Manager overview with the main sections of the article

The Licensing Manager sells software by subscription. A product that uses it gives every service a licence key instead of an account on a server. The licensed program (intoPanel, CoreInto itself, or any program you write a check into) sends its key to a public check address on your site, and the answer tells it whether it may run. Every answer is signed with a key pair that belongs to your installation, so the program can trust an answer, and keep trusting it for a while, even if your site cannot be reached. The licence follows the service: when the service is suspended, the licence is suspended; when the service closes, the licence expires. The admin pages are under Add-ons; the client sees the key on the service page in the client area.

What you need

ItemWhere
Permission "Use add-on modules" (addon_modules)Needed for every page under Add-ons. Set it on the role under Settings > Administrator Roles.
The encryption key in configuration.phpThe private signing key is stored encrypted with it. Without it the Licensing Settings page shows "The encryption key is not loaded" and checks cannot be signed.
PHP's openssl extensionMakes the key pair and signs answers. It is required by CoreInto anyway.
A product with the module "Licensing Manager (licence keys)"See the next section.

No database change is needed: the licenses and license_checks tables are part of the base schema.

The signing key pair

Your installation has one key pair (ECDSA P-256) for signing licence answers. It is made the first time it is needed: the first time a program checks a key, or the first time somebody opens Add-ons > Licensing Settings, whichever comes first. The pair is kept in two settings that have no form of their own:

SettingWhat it holds
LicensingPrivateKeyThe private key, sealed with the encryption key from configuration.php. It never leaves the server; it is not shown on any page.
LicensingPublicKeyThe public key (PEM). It is shown on Add-ons > Licensing Settings so you can put it into the licensed program.

Add-ons > Licensing Settings shows:

  • Check address: the public address programs send their key to (see "The check address" below).
  • Sent by the software and Answers: a short reminder of the request fields and the possible results.
  • Products that sell licences: every product with the licensing module, with its key prefix, IP limit, whether the domain and folder are locked, whether clients may reissue, and the days between checks. The product name links to the product's Setup tab.
  • Public key: the PEM text to paste into the licensed program. "It can only check answers, not make them."
  • Check code for the software (PHP): a ready-made PHP function (license_check() and license_read()) with your check address and public key already filled in. Copy it into the program as a starting point.
  • Make a new key pair: a folded section with a warning, a confirmation box ("I understand; make a new key pair") and the button. Use it only if the private key may have leaked. Every program that holds the old public key stops trusting your answers until it is given the new one. The action is written to the activity log as "Made a new licence signing key pair". Without the tick the page refuses with a message.

Keep a copy of configuration.php with your database backups: the private key in the database can only be opened with the encryption key in that file (see Backups and updates).

A product that sells licences

Open Settings > Products/Services (see Catalogue), open or add the product, and on its Setup tab choose the Module "Licensing Manager (licence keys)". The server group does not matter for this module: no server is used. When the module is chosen the Setup tab shows these fields:

FieldWhat it doesDefault and limits
Licence key prefixLetters put in front of each new key, such as IP- for intoPanel or CI- for CoreInto. Only letters, digits and hyphens are kept; letters are made upper case; at most 20 characters.empty
Server addresses (IPs) one licence may useHow many different server addresses the licence may be used from. The first checks fill the list.1 (1 to 1000)
Lock the licence to the domain of the first checkWhen ticked, the domain sent with the first check is remembered and later checks must send the same domain.off
Lock the licence to the folder of the first checkWhen ticked, the folder (dir) sent with the first check is remembered and later checks must send the same folder.off
Clients may reissue their licence themselvesShows the "Reissue Licence" button on the client's service page.off
Reissues a client may make (0 for no limit)How many times a client may reissue. Staff reissues do not count.0 (0 to 1000)
Days the software may run between checksPut in each active answer as valid_until, so the program keeps working when your site cannot be reached.7 (1 to 365)

Two more fields on the Setup tab matter for licences:

  • Set up automatically: when the licence is issued. "On order" issues it as soon as the order is placed, "On payment" when the first invoice is paid, "When the order is accepted" when staff accept the order, and none leaves it to staff. The same choices as for a hosting product.
  • Welcome email: choose "New licence key" (the template license_welcome), or your own template of the kind "Products and services". With "None" no email is sent and the client finds the key on the service page.

A price, a product group and the usual ordering settings are set as for any product. Nothing on the product asks for a domain; a licence does not need one.

How a licence is issued

Setting up the service issues the licence. This happens at the moment the product's "Set up automatically" says, or when staff press Create under "Server commands" on the service page (with the box "Email the client" deciding whether the welcome email goes out), or accept the order with "Create the accounts on the server" ticked. CoreInto then:

  1. Makes a key: the prefix followed by four groups of five characters, separated by hyphens, for example CI-K7MPT-3WQ9X-H2RDN-8ZVBC. The characters are upper-case letters and digits without 0, O, 1 and I, so a key can be read out over the phone. A key is checked for uniqueness before it is saved.
  2. Saves the licence with status Active, empty lists of addresses, domains and folders, the product's IP limit, and a reissue count of 0.
  3. Marks the service Active if it was Pending.
  4. Writes "Licence issued: KEY" in the service's log.
  5. Sends the product's welcome email, unless staff untick "Email the client" when pressing Create.

Each service has exactly one licence. Setting up a service that already has a licence does not make a new key: a suspended or expired licence is made Active again and the same key is kept.

Licences are not brought across by the import from another billing system; services of a licensing product that come from an import have no key until they are set up in CoreInto.

The licence follows the service

What happens to the serviceWhat happens to the licence
Suspended (by staff, or by the overdue run)Suspended. Checks answer suspended.
UnsuspendedActive.
Terminated or cancelledExpired. Checks answer expired.
Set up againActive again, same key.

A check also looks at the service itself: if the service is not Active or Suspended (for example Pending or Fraud), the answer is expired whatever the licence says.

Licence statuses as shown on the pages:

StatusMeaning
ActiveThe licence is valid.
Reissued (binds at the next check)The binding was cleared; the next check that passes fills the lists again and sets the status back to Active.
SuspendedThe program is told the licence is suspended and to contact the seller.
ExpiredThe program is told the licence has expired.

Staff may set the status by hand on the licence page, but the next service action (suspend, unsuspend, close) sets it again.

Binding

A licence is tied to where the program runs. The lists are empty when the key is issued and are filled by the first checks:

  • IP addresses: the address the check came from (the caller's address as the web server sees it). Each new address is added until the list holds as many as the licence allows; a check from a further address then answers wrong_ip. The limit is the product's "Server addresses (IPs)" at the time of issue and can be changed per licence by staff.
  • Domains: used only when the product locks the domain. The domain sent with the first check is kept; later checks must send the same one or get wrong_domain. Domains are compared in lower case, without a scheme (https://), port, path or a leading www., so https://www.Example.com:8443/panel and example.com are the same domain.
  • Folders: used only when the product locks the folder. The dir sent with the first check is kept; later checks must send the same folder or get wrong_directory. Backslashes are turned into forward slashes and a trailing slash is dropped.

If the domain or folder lock is turned on after a licence already has empty lists, the next check fills them. If a check sends no domain or folder, nothing is bound for that list and the check passes.

Staff can edit all three lists on the licence page. Clients cannot edit them; they can only clear them by reissuing.

The check address

The address is shown on Add-ons > Licensing Settings. With short addresses it is https://your-site.example.com/license/check; with the basic address style it is https://your-site.example.com/index.php?r=/license/check. It needs no sign-in.

What the program sends

A POST with form fields, a POST with a JSON object as the body, or a GET with the fields in the query string. The fields:

FieldRequiredMeaning
keyyesThe licence key. Spaces around it are dropped and it is made upper case.
domainnoThe domain the program runs on (for example the web server's host name). Needed when the product locks the domain.
dirnoThe folder the program is installed in. Needed when the product locks the folder.
versionnoThe program's version, up to 32 characters. Shown on the licence page and in the check log.
noncenoAny text up to 64 characters. It is copied into the answer, so the program can be sure the answer is for this request and not an old one replayed.

The server address is not sent: it is the address the request comes from.

What the program receives

The reply is JSON with two members:

{"data": "<the answer as JSON text>", "signature": "<base64>"}

data is the answer as a JSON string, and signature is an ECDSA P-256 signature with SHA-256 over the exact bytes of that string, encoded in base64 (DER format, as PHP's openssl_sign makes it). The program must check the signature over data with the public key from Licensing Settings before reading it (in PHP: openssl_verify($data, base64_decode($signature), $publicKey, OPENSSL_ALGO_SHA256) === 1). The sample code on the settings page does this.

The answer inside data always has:

MemberMeaning
statusOne of the results below.
messageA sentence for the program to show.
keyThe key as checked.
checked_atThe time of the check, UTC, for example 2026-10-09T14:03:21Z.
nonceThe nonce the program sent (empty if none).

When status is active the answer also has:

MemberMeaning
valid_untilThe time until which the program may trust this answer offline: now plus the product's "Days the software may run between checks".
productThe product name.
registered_toThe client's company, or the client's name when there is no company.
next_due_dateThe service's next due date (YYYY-MM-DD), empty when none.
ips, domains, directoriesThe bound lists after this check, each a JSON array of strings.

The results and the message that goes with each:

statusmessage
activeThe licence is valid.
invalidThere is no such licence key.
suspendedThe licence is suspended. Please contact the seller.
expiredThe licence has expired.
wrong_ipThe licence is in use on another server (IP address). Reissue it in the client area to move it.
wrong_domainThe licence is in use on another domain. Reissue it in the client area to move it.
wrong_directoryThe licence is in use in another folder. Reissue it in the client area to move it.

The checks are made in that order: an unknown key is invalid; then suspended; then expired; then the address; then the domain; then the folder. Only an active check updates the bound lists. Every check, whatever its result, updates the licence's "last check" time, address and version, and is written to the check log.

Two other replies are not signed:

  • HTTP 429 with {"error": "Too many checks with unknown keys from this address. Try again in 15 minutes."}: an address that sent 30 unknown keys within 15 minutes is refused for 15 minutes. Checks of known keys do not count.
  • HTTP 503 with {"error": "The licence server cannot sign answers right now."}: the key pair could not be used, usually because the encryption key is not loaded. The error is written to the error log.

What the program should do

The sample code on Licensing Settings shows the intended use: send a fresh nonce with each check; accept the answer only if the signature is right and the nonce matches; save the whole signed reply; while your site cannot be reached, read the saved reply again (checking its signature) and keep running until its valid_until. With the default of 7 days, a program whose licence server is down keeps working for a week.

Reissuing a licence

Reissuing clears the IP addresses, domains and folders. An Active licence becomes "Reissued (binds at the next check)"; a Suspended or Expired licence keeps its status but its lists are cleared too. The next check that passes binds the licence to the server, domain and folder it came from and makes it Active again. The key does not change.

WhoWhereCounts towards the client's limit
StaffThe Reissue button on the licence page under Add-ons > Licensing Managerno
Another programThe API action licenses.reissueonly with count_for_client
The clientThe "Reissue Licence" button on the service page in the client areayes

A client may reissue only when all of these hold; otherwise the service page shows the reason instead of the button:

  • The product allows it ("Clients may reissue their licence themselves"). Otherwise: "Please open a support ticket to move this licence."
  • The licence is Active or Reissued. Otherwise: "Only an active licence can be reissued."
  • The client has not reached the product's limit. Otherwise: "This licence has been reissued as many times as allowed. Please open a support ticket."
  • The signed-in user has the account permission "Manage services".

Each reissue is written to the activity log ("Reissued licence KEY") against the client.

The email "New licence key" (license_welcome)

The template license_welcome, kind "Products and services", is the welcome email meant for licensing products. Its default subject is "Your {{product_name}} licence key" and its body shows the key in a table, tells the client to enter it when installing the program, says that the licence binds to the server it is first used on and can be moved by reissuing from the client area, and gives the next payment. Edit it under Settings > Email Templates (see Email).

Merge fields beyond the common ones (client_first_name, company_name and so on):

FieldValue
{{license_key}}The key. It is also available to every other product email, empty when the service has no licence.
{{product_name}}The product.
{{next_due_date}}The next due date, in the company's date format.
{{recurring_amount}}The recurring price, formatted in the client's currency.
{{billing_cycle}}The billing cycle, for example Monthly.

The email is sent only when the product's Setup tab names it as the Welcome email.

The admin pages (Add-ons)

All four pages need the permission "Use add-on modules".

Add-ons > Licensing Manager

A list of every licence, newest first, 50 a page. Columns: Licence key (links to the licence page), Product (links to the service; the service's domain in small print), Client (links to the client), Bound to (the IP addresses and domains, or "not yet"), Last check, Status.

Above the list a line shows how many licences are in each status; each number is a link that filters the list. The filter form has:

  • Search: matches the key, the bound IP addresses and domains, the service's domain, the client's email, company or name.
  • Status: Any, Active, Reissued, Suspended, Expired.

Buttons at the top open Licence Checks and "Settings and key" (Licensing Settings). If no product uses the module yet, a notice explains how to choose it on a product's Setup tab.

One licence

Open a key from the list. The page title is the key with its status badge, and a Reissue button at the top right (no confirmation; a message confirms that the lists are cleared and the next check binds again).

The Licence card shows the product (link to the service, with the service number and status), the client (link, with company), when the key was issued, the last check (time, address and version, or "never"), reissues by the client (with the product's limit, or a note that clients cannot reissue this product), and a summary of the product's rules: how many IP addresses, whether the domain and folder are locked, and the days between checks.

The "Where it may run" card is a form:

FieldWhat it does
IP addresses (one per line)The bound addresses. Each line must be an IP address; otherwise the page shows "Not an IP address: ..." and nothing is saved.
Domains (one per line)The bound domains, cleaned as described under Binding.
Folders (one per line)The bound folders.
IP addresses allowedThe limit for this licence (1 to 1000), copied from the product when the key was issued.
StatusActive, Reissued, Suspended or Expired. The help text reminds you that the status also follows the service.

Lines may also be separated by commas; duplicates and empty lines are dropped. Empty lists are filled by the next check. Saving writes "Changed licence KEY" to the activity log.

Below, "Recent checks" lists the last 50 checks of this licence: time, result (green for active, red otherwise), IP, domain, folder and version.

Add-ons > Licence Checks

Every check of every key, newest first, 100 a page, including checks with unknown keys (those show the key sent but without a link). Filter by Search (key, IP or domain) and by Result (any of the seven results). Rows older than 90 days are removed by the automation task prune ("Remove old records"), which also trims the other logs; see below.

Add-ons > Licensing Settings

Described under "The signing key pair" above.

The client's service page

In the client area, Services > the service (/client/services/ID) shows a Licence card for a service of a licensing product:

  • Licence key, in a monospaced font.
  • Status, as a badge.
  • IP Address, Domain and Folder: each shown only when bound.
  • Last check: shown once the program has checked in.
  • "Reissue Licence", with the sentence "Moving the software to another server, domain or folder? Reissue the licence; it binds again at its next check." It appears when the licence is Active or Reissued and the user has the "Manage services" permission; when the client may not reissue, the reason is shown instead (see "Reissuing a licence").

The rest of the page is the ordinary service page (see Services and servers): price, cycle, next due date, add-ons, upgrade and cancellation. There is no "sign in to control panel" button, since a licensing product has no server.

The API

Three actions of the JSON API (/api/v1/ACTION, Bearer credentials; the reference is on Settings > API Credentials and in API):

ActionScopeParametersReturns
licenses.listservices:readclient_id, service_id, status (active, reissued, suspended, expired), limit, offsetlicenses: id, service_id, client_id, license_key, status, allowed_ips, allowed_hosts, allowed_dirs (as arrays), max_ips, reissues, last_check_at, last_ip, last_version, created_at; newest first
licenses.getservices:readid, key or service_idlicense: the licence with its service, product and client fields, plus checks: the last 20 checks (created_at, result, ip, host, dir, version)
licenses.reissueservices:writeid, key or service_id; count_for_client (true to count it towards the client's reissue limit)license: the licence after the reissue

licenses.reissue is written to the activity log as "Reissued licence KEY through the API". Without id, key or service_id the answer is 400 "Give id, key or service_id."; an unknown licence is 404.

Version 2: signed tokens, product codes and releases

Version 2 (CoreInto 0.30) is what intoPanel, StackRegent and later CoreInto itself use. The full contract between this site and the programs is docs/LICENSING-SPEC.md in the repository; this section is what you see and set here. The first check address (/license/check) keeps working unchanged for software that already uses it.

The product's extra settings (Setup tab)

FieldWhat it does
Product codecoreinto, intopanel or stackregent (any lower-case code works). The program sends it with every check; a key sold for another product answers wrong_product. Releases are matched to licences by it.
Lock the licence to the machine of the first checkThe program sends a fingerprint of its server (a SHA-256 of /etc/machine-id on Linux, of the MachineGuid on Windows; the raw value never leaves the server). Afterwards another machine answers wrong_machine. Turn it on for the panels.
EditionA name the program shows, such as "Unlimited".
Accounts allowed0 for no limit. Sent to the program, which refuses new accounts above it. The program reports how many it has; the licence page shows it, marked "over the limit" when it is.
FeaturesComma-separated names the program turns on.
Trial licence (one per server)Each address and each machine may use one trial of the product code, ever (remembered in license_trials, even after the trial ends or is reissued). A second one answers trial_used. Clients cannot reissue a trial. Sell a trial as a free product with a fixed term (for example 10 days).
Grace days after a missed checkHow long the program keeps working after valid_until when this site cannot be reached. Usually 7.
Install command shown to the clientShown on the client's service page with a copy button; {key} becomes the licence key.

The addresses

AddressUsed for
POST /license/v2/checkActivation and the daily check. JSON in; {"token": "..."} out: a standard JWT signed with ES256 (the same key pair as version 1), whose header names the key by kid.
POST /license/v2/releasesThe program asks for releases newer than its version. Only a licence already bound to that server gets them (otherwise not_activated). Each release comes with a download link that works for 15 minutes, for that licence only.
GET /license/v2/download/...The link itself. Wrong, changed or old links answer 404 and count towards the address's limit of unknown tries.

Version 2 answers only for products with a product code: a licence of a product without one answers wrong_product. A check without a nonce of 16 to 64 characters gets HTTP 400 and no token. Check-ins of one licence are decided one at a time (the licence row is locked), and trials of one product code too, so two servers checking in at the same moment cannot both bind.

Both addresses keep working in maintenance mode, as payment notifications do. Their checks are in Licence Checks with the API shown (v2, v2-rel); downloads appear there with the result download.

Release keys and Add-ons > Licensing Releases

Releases are signed by you, on your own computer, never on this site:

  1. Once per product: php tools/release.php keygen-product --private=intopanel-release.pem --public=intopanel-release.pub. Keep the private file offline. Paste the public one into Licensing Settings > Release keys with the product code, and build it into the program.
  2. For each release: php tools/release.php sign-product --product=intopanel --version=0.2.0 --file=intopanel-0.2.0.tar.gz --key=intopanel-release.pem prints the signature.
  3. Add-ons > Licensing Releases > Add Release: product code, version, channel (Stable or Beta), the file (upload; or, when it is larger than PHP's upload limit, put it in storage/releases/incoming over FTP and choose it), the signature and notes. The file is refused unless the signature matches it with the product's release key. It is moved into storage/releases/PRODUCT/ (never served directly).
  4. Publish it. Active licences of that product code are offered it; Beta also offers Stable releases. Withdraw takes it back; Delete removes the row and the file.

The client's service page shows "Download version X" for the newest published stable release while the service and licence are active, and the install command when the product has one.

Licensing Settings and the licence page

Licensing Settings also shows the version 2 addresses, the current key id (kid) and the retired ones (making a new key pair keeps the old public key as retired), and the release keys by product code.

One licence shows the product code, edition and trial mark, the server name the program reported, accounts in use, the machines it is bound to (editable, 64 hexadecimal characters each), the machine of each check, and how many addresses and machines checked in successfully over 7 days. When that is more than the licence allows (plus its reissues), a warning says the key may be shared between servers.

The API's licence answers now also carry allowed_machines, usage_accounts and last_hostname, and the checks in licenses.get carry machine and api.

Housekeeping

TaskWhat it does
prune (Remove old records)Among other things, deletes rows of the check log older than 90 days, at most 50,000 per run. Runs daily at the hour set in Settings > Automation Settings. The licence itself keeps its last check time, address and version.

The activity log (Utilities > Activity Log) records new key pairs, staff changes to a licence, and every reissue by staff, client or API, each with the client when there is one. Programs' checks are not in the activity log; they are in Licence Checks.

Frequently asked questions

What does "Licensing Manager" cover in CoreInto?

The Licensing Manager sells software by subscription. A product that uses it gives every service a licence key instead of an account on a server. The Licensing Manager sells software by subscription. A product that uses it gives every service a licence key instead of an account on a server. The licensed program (intoPanel, CoreInto itself, or any program you write a check into) sends its key to a public check address on your site, and the answer tells it whether it may run. Every answer is signed with a key pair that belongs to your installation, so the program can trust an answer, and keep trusting it for a while, even if your site cannot be reached. The licence follows the service: when the service is suspended, the licence is suspended; when the service closes, the licence expires. The admin pages are under Add-ons; the client sees the key on the service page in the client area.

Where do I find more help?

Use the Help button on this site, browse the Administrator guide on coreinto.com, or submit a ticket at https://system.coreinto.com/contact with your panel version and the steps you tried.

Submit a ticket