Self-Hosted Deployment

Deployment

Self-Hosted Deployment (Enterprise)

A self-hosted Enterprise instance runs the exact same PermitCore.Api/ PermitCore.Web.Admin/PermitCore.Web.Public images PermitCore runs for its own SaaS — there is no separate self-hosted build target. Self-hosting is enabled purely through environment variables, and a self-hosted tenant owns the entire stack — database, signing keys, everything — from day one.

Prepare the deployment

Arrange Enterprise provisioning before installation. You need a host that can run the supplied containers, persistent database storage, DNS and HTTPS for your API and web frontends, access to the private image registry, and a configured mail provider for account emails.

  1. Choose the API, admin and public-site hostnames. Configure their URLs and the allowed origins consistently in the supplied environment file.
  2. Set the trusted API host in both SELF_HOST_TRUSTED_API_HOST and ALLOWED_HOSTS. A hostname missing from either allowlist is rejected even if DNS and TLS work.
  3. Keep database credentials, signing material and registry access outside source control. Save the one-time self-host license securely before closing the provisioning screen.
  4. Back up the database and the secrets needed to decrypt its protected fields. Test a restore before relying on the instance for customer licensing.

How it works

  1. Requesting a deploymentPOST /api/v1/selfhost/request (CompanyAdmin+, gated behind the self_hosting feature key, Enterprise-only) creates a SelfHostLicense row (SH-XXXX-XXXX-XXXX-XXXX format key, hashed the same way as customer license keys — plaintext is never persisted). One active self-host license per tenant is enforced at the database level.
  2. Provisioning — a SuperAdmin reviews the request at /admin/selfhost, issues a scoped GitHub token for the private image registry, and provisions the instance. The customer then downloads a pre-filled .env (plaintext key shown once) and runs the provided docker-compose.selfhost.yml.
  3. Check-in — the running instance calls a public check-in endpoint daily. This is a no-op unless SelfHost:LicenseKey is configured, so the same image is harmless running as PermitCore's own SaaS.
  4. Continuity tokens — for the "PermitCore the company disappears" case, a SuperAdmin can issue a long-lived (commonly 20-year) signed continuity token, using the same per-tenant ECDSA P-256 keypair already used for offline activation tokens. Once configured in the instance's .env, it's verified entirely locally — zero network calls — and bypasses check-in while the continuity token is valid.
  5. Distribution — private container images, per-customer pull-token issuance handled by a SuperAdmin. Revoking a pull token blocks new image pulls only — it never touches an already-running container.
Effect of a self-host license lapse: the check-in path is a separate code path from the public /api/v1/validate//activate endpoints a self-hosted instance's own downstream customers call — different entity, different hash namespace, zero shared references. If a self-host license lapses, the only effect anywhere is a banner plus a reseller-feature flag disabled locally on that instance, after a 30-day grace period. The instance's own customers' license validation and activation are not disabled by that self-host check-in failure. They still depend on the instance being healthy and the individual customer's license rules.

Key versioning — rotation vs. emergency response

Routine key rotation and "this key may have leaked" are different situations with different blast radius, so they're two different endpoints on purpose — an earlier design that only offered one destructive action meant any rotation forced every offline-installed customer to reactivate, even when nothing was actually wrong.

ActionWhat happensWhen to use it
POST /api/v1/signing-key/rotate Current Active key becomes Deprecated, a new key becomes Active. No tokens are revoked — a Deprecated key stays fully valid for verifying tokens it already signed. Safe to run on a schedule — annual hygiene rotation, offboarding an engineer who had export access — without disrupting any already-running offline install.
POST /api/v1/signing-key/regenerate Current key becomes Compromised — rejected outright at verification regardless of any individual token's own status. Every currently-tracked, non-expired offline token for the tenant is also revoked. Use after a suspected compromise. Requires an explicit confirmation flag. Distribute the replacement trusted key and tokens to affected installations.

GET /api/v1/signing-key/versions returns a read-only history of every key version (id, status, timestamps, no key material) for a vendor to audit their own rotation history.

An already disconnected verifier cannot learn that a key was compromised. Server-side revocation takes effect only where the new trust state is available; plan how offline installations receive key-status updates and replacement tokens.

Verify and operate the instance

  • Confirm admin sign-in, verification/reset emails, product creation and license activation against your own API hostname.
  • Check validation from a customer application and test an offline token with the instance's trusted signing key.
  • Monitor API availability, database capacity, mail delivery, backups and certificate expiry.
  • Before upgrades, review release instructions, take a restorable backup and verify the integration in a staging instance.