Self-host Enterprise

The community self-host bundle is the free edition of the product: full 9-module suite, full agent, full knowledge graph. It also requires a LICENSE_KEY — a free, self-serve, 1-seat, 1-year community-tier JWT minted by our cloud signing service. The same offline verifier handles both tiers; the JWT's tier claim is what decides what unlocks.

What community doesn't include is the enterprise tier: SSO/SAML, ABAC, deep audit search/export, system-admin DSAR queue. Those features physically ship in the binary, but are gated at runtime by the license tier — only an enterprise-tier JWT unlocks them.

Self-host Enterprise is for the case where you want those features on-prem (compliance, security clearance, customer firewall) without giving up the data-locality story.

What you get

FeatureSelf-host communitySelf-host Enterprise
9 modules + agent + knowledge graph
File projection (memory.md, skills/)
BYO LLM key / bundled Ollama
Manual backup / restore CLI
SSO/SAML (Google Workspace, Entra, Okta, OneLogin, custom)
ABAC (attribute-based access-control policies)
Audit deep (tenant-wide search + signed exports)
System-admin DSAR queue (operator-only — bulk fulfillment, cross-user export, super-admin portal)❌ no super-admin portal at all
User self-serve DSAR (Settings → Privacy)❌ license-gated
Free community LICENSE_KEY (1 seat, 1 year, renewable)n/a — replaced by paid enterprise key
Paid enterprise LICENSE_KEY (seat-counted, feature-set claim)

The self-serve DSAR row matters: even community self-host is GDPR- compliant out of the box. The enterprise gate is on the system-admin (operator-side) DSAR queue console — the super-admin portal at admin.<basedomain> where the platform operator handles cross-user bulk operations. Tenant admins and users always have self-serve, on every edition.

Getting a license

Email sales@ccdsuite.com with:

  • Organization name
  • Expected user-seat count
  • Desired feature set (any of: sso_saml, abac, audit_deep, dsar_self_serve, dsar_admin_queue, multi_org, custom_retention)
  • License term (12 / 24 / 36 months)
  • Optional: hostname binding (locks the license to a specific deploy domain — set to your production hostname for tighter anti-piracy)

You'll receive a signed JWT and a one-pager with the install steps.

Installation

Three env-var changes turn a community install into an enterprise install. No new container image needed — the same binary verifies the license at boot.

# In your .env / docker-compose override / k8s secret:
EDITION=selfhost-enterprise
LICENSE_KEY=eyJhbGciOiJFZERTQSJ9.eyJ0aWVyIjoi...
# LICENSE_PUBLIC_KEY is optional — used only for staging/dev where you
# want to test with a self-issued license against a non-production key.

Restart the web service. On boot you'll see (real format from the instrumentation hook):

[license] OK — tier=enterprise customer="Acme Co" features=[sso_saml,abac,audit_deep] expires_in_days=362

If the license is missing/expired/invalid, you'll see a multi-line banner:

──────────────────────────────────────────────────────────────
 CCD Suite — licence key required (reason: expired).
 Self-host needs a key to run. Get a FREE community key in seconds:
   https://ccdsuite.com/self-host
 then set LICENSE_KEY in your .env and restart.
──────────────────────────────────────────────────────────────

Boot behavior depends on NODE_ENV:

  • Production (NODE_ENV=production): the app exits with code 1 after printing the banner. Your container manager (Docker, k8s, systemd) will see the failure and crash-loop. Fix the key and restart.
  • Development (NODE_ENV != production): the app logs the banner but continues to boot so you can debug locally without a valid key.

Pick NODE_ENV deliberately when you set up your environment — a production deploy with NODE_ENV unset will still hard-block (the default is production-strict).

Verifying the install

The admin License page lives at GET /api/admin/license/status and returns the live verification result:

The route requires an authenticated super-admin session (cookie- based, not Bearer tokens). The easiest way to call it from the CLI is to copy your sb-access-token cookie from the browser DevTools and pass it in:

curl -s -b "sb-access-token=$SUPER_ADMIN_COOKIE" \
  https://admin.your.deploy.example.com/api/admin/license/status | jq

(Or just hit the admin License page in a browser — the JSON renders inline.)

Sample output (enterprise, valid):

{
  "edition": "selfhost-enterprise",
  "mode": "enterprise",
  "valid": true,
  "customer": "Acme Co",
  "features": ["sso_saml", "abac", "audit_deep"],
  "seats": 100,
  "domain": null,
  "expiresAt": 1782345600,
  "daysRemaining": 362,
  "licenseId": "a8e86c79-10f0-47e2-b4c1-76b1931da815"
}

When a gated route is hit without a valid license, callers get a typed JSON error:

{
  "error": "feature_not_entitled",
  "feature": "sso_saml",
  "reason": "no_license",
  "hint": "This feature requires a valid enterprise license. Set EDITION=selfhost-enterprise and LICENSE_KEY in your environment."
}

Status code is 402 Payment Required when the license is missing/expired/invalid, and 403 Forbidden when the license is valid but doesn't include the requested feature (i.e. you need to upgrade your plan, not renew it).

Renewal

Licenses are short-lived by design (12 months is the default term). 30 days before expiry, an automated email goes to the address on file with renewal instructions. The cron that drives this lives on the cloud platform side; your install doesn't need to do anything to opt in.

Once you have the renewed JWT, swap the LICENSE_KEY env var. The verifier caches results for 60 seconds, so the new key picks up within a minute on its own — a restart is optional but immediate. Old ciphertext / audit entries / DSAR requests remain accessible (the license gates the features, not the data — data is yours regardless).

Domain binding — read this before you set it

If you ask sales to bind your license to a hostname (the domain claim), the verifier hard-rejects every request that doesn't match. This is one-way: changing your deploy domain after issuance requires requesting a brand-new license. Don't set domain unless you have a stable production hostname you don't expect to change.

Offline verification

Verification is offline. The license is a JWT signed by our Ed25519 private key; the matching public key is baked into the binary. Your install does not phone home to verify. This makes Self-host Enterprise viable for:

  • Airgapped customer environments (gov / defense / regulated finance)
  • Customer networks that block outbound to the public internet
  • High-availability deploys where a licensing-server outage shouldn't affect you

The trade-off is no online revocation. We mitigate this with short license lifetimes and a renewal cadence that matches your business relationship — if you need true online revocation, talk to us about the cloud edition.

Troubleshooting

Boot log shows [license] FAILED — reason=invalid_signature. The LICENSE_KEY value doesn't verify under the embedded public key. Common causes:

  • Pasted the JWT from email with a soft-wrap line break in the middle. Confirm no whitespace within the token.
  • Using a staging license against a production-built binary, or vice versa. Make sure the issuer matches the binary's bake.
  • Pre-production build with a placeholder public key. Set LICENSE_PUBLIC_KEY env var to the matching public-key.pem.

Boot log shows [license] FAILED — reason=expired. Renew the license (sales@ccdsuite.com). The data isn't lost — features just refuse new operations until renewed.

Boot log shows [license] FAILED — reason=domain_mismatch. The license was issued with a domain claim bound to a specific hostname and your deploy isn't on that host. Either deploy on the bound hostname, or request a domain-unbound license (less anti-piracy, more flexibility).

Settings → Security → SSO page is empty. The admin nav hides enterprise feature pages when the entitlement check returns false. Confirm EDITION=selfhost-enterprise is set (not selfhost), and the boot log shows [license] OK. If the license is valid but the page is still hidden, hit GET /api/admin/license/status to verify the feature is in the license's features array.

Going back to community

Remove LICENSE_KEY from the environment and set EDITION=selfhost. Restart. Enterprise features will be hidden in the UI and denied at the API layer. No data is deleted; if you later restore the license, every SSO config / ABAC policy / audit row remains in place.

Read next