Developer Guide

Licensing

Cendia needs a license to serve a public host. How keys work, where they go, what is enforced, and why your local machine needs nothing.

Local development needs no license. Clone, run, build a site, publish, break things — none of that asks you for a key. A license matters only once an installation answers on a host that is not local.

That is the whole shape of it. The rest of this page is detail.

What counts as local

Judged by the host name in the request, not by where the connection came from:

  • localhost, 127.0.0.1, ::1
  • any *.localhost name — arena.dev.localhost, partner.dev.localhost, whatever you invent
  • private network addresses: 10.x, 172.16–31.x, 192.168.x, and the IPv6 equivalents

Everything else needs a license, including staging.customer.com and a bare public IP.

The "host name, not connection address" rule is the one worth remembering. Behind a load balancer every request arrives from a private address, so judging by the connection would make every hosted site count as local and the license would mean nothing. www.customer.com is never local, whatever it resolves to.

If your proxy rewrites the Host header rather than forwarding it, turn on forwarded headers — the same setting the Geographic location criterion needs:

{
  "Cendia": {
    "ForwardedHeaders": {
      "Enabled": true,
      "KnownProxies": [ "10.0.0.4" ]
    }
  }
}

The three kinds of key

Trial Developer Production
Runs on any host its named root domain and subdomains its named root domain and subdomains
Term 10 or 30 days 90, 180 or 365 days the contract's end date
For evaluating Cendia partners and implementation work a live customer site

A trial also carries a daily request allowance, stated when the key is issued. What counts towards it is what the license gates: rendered pages and Delivery API calls. Working in /cms, previewing, and the presence endpoint do not count — evaluating the CMS should not spend the allowance you are evaluating it with.

Spending it pauses publishing until midnight UTC. The site keeps serving, as it does in every other licensing state.

Installing a key

Two routes, and configuration always wins.

Settings → Configuration → License is the normal one. Paste the key, press Install. It is verified before it is saved, and the card then shows what it says: type, who it is for, which host it covers, when it ends, how many websites it allows. Every key ever installed stays in the history below the card — including ones you replaced — so an installation can answer "what were we running in March?" without asking us.

Host configuration is for cloud tenants and automated deployments:

{
  "Cendia": {
    "License": {
      "Key": "eyJhbGciOiJFUzI1NiIsImtpZCI6..."
    }
  }
}

Or Cendia__License__Key as an environment variable, which is usually the better home for it in a container. When the host supplies a key, the Settings card goes read-only and says so — an administrator should not be able to half-replace something the next restart will put back.

A license is not a secret. It is signed, so it is tamper-evident on its own, and an administrator needs to be able to read what theirs says. It is stored in the clear in cms_Licenses, and it does not belong in your secret store.

What is actually enforced

A licensed site is never taken offline. Whatever is wrong with the license, pages keep serving:

Situation What happens
No license, local host Everything works. This is your machine.
No license, public host Pages do not render (503). /cms and /preview stay reachable so you can install a key.
Valid license Nothing to see.
Expired Production license An alert in /cms. Nothing is restricted — renew when you can.
Expired Trial or Developer license An alert, and publishing is paused. Editing and the live site are unaffected.
Trial allowance spent for today An alert, and publishing is paused until midnight UTC. The site keeps serving.
License for a different domain An alert. Nothing is restricted.
Website cap reached Creating another website is refused. Existing ones are untouched.

The alert appears as a strip under the action bar on every authoring screen, not only in Settings, because an expired license is something the person it stops from publishing needs to see.

An installation that has never been licensed is the single case where Cendia refuses to serve. There is no customer site to interrupt, and without that the lock would mean nothing.

What Cendia never does

  • It never calls home. No telemetry, no activation, no license server the site depends on. Verification is entirely offline: the public key that checks the signature ships inside the package. We ask your site instead — see below.
  • It never takes a licensed site down. See the table above.
  • It never puts a notice on your public pages. Nothing in your rendered HTML says anything about licensing, in any state.

The presence endpoint

Your installation answers one documented URL:

GET /.well-known/cendia/license
{
  "product": "cendia",
  "status": "valid",
  "type": "production",
  "expires": "2027-03-31",
  "licence": "9f2c41ab77de"
}

status is valid, expired, invalid or unlicensed. licence is a truncated hash of the license id — we can match it against our own records; it is of no use to anyone else.

That is the entire response. Not your customer's name, not your domain, not how many websites you run, and not your Cendia version — an endpoint that named the version would be a convenient way for someone to find installations running a release with a known flaw.

This is the trade we made instead of a call-home. Nothing inside your CMS contacts us on a schedule, and nothing you run depends on our servers being available. When we need to know whether a license is still in use, we ask the site, the same way anybody's monitoring would.

It is cached for five minutes and rate-limited. If you would rather not answer at all:

{
  "Cendia": {
    "License": { "EnablePresenceEndpoint": false }
  }
}

The path then stops existing. Our reports say "unreachable" for your site, which is not the same as "not in use" — an installation on a private network is unreachable for the same reason, and that is fine.

How verification works

A key is a signed token — a readable JSON body plus a signature. The package holds public keys only, so Cendia can check a license and has no code that could produce one. Signing happens on our side, against a key that cannot leave its vault.

That means:

  • Editing a key invalidates it. There is nothing in it worth editing anyway — the claims are the ones above.
  • A key signed by anything other than our signing key is refused.
  • A key from a newer Cendia that demands a limit your build cannot enforce is refused rather than run without the limit. The message names the claim and tells you to upgrade.

Troubleshooting

"This site is not licensed" on a host you expected to work. The host name is not local and no valid key is installed. Check Settings → Configuration → License; if the card shows an error rather than claims, the key did not verify.

Everything works locally and fails on staging. Expected, unless staging is a subdomain of the licensed root domain. staging.customer.com is covered by a license for customer.com; customer-staging.net is not.

Publishing is refused but the site is fine. An expired Trial or Developer license. Install a current one.

A key rejected as "signed with a key this build does not know". Either the key is not ours, or it was signed with a signing key newer than your build. Upgrade Cendia, or ask us for a key issued against the key your version carries.

Docker and CI. Containers usually answer on a container hostname, not localhost. Either map the port and browse via localhost, or supply a key through Cendia__License__Key. Test suites that drive the site through a non-local host name need a key for the same reason.

Getting a key

Trials are self-service once the licensing portal ships; until then, ask us. Developer licenses are issued to partners on request, and production keys are issued against a signed contract for a named root domain.