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
*.localhostname —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.