Self-Hosted 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.
- Choose the API, admin and public-site hostnames. Configure their URLs and the allowed origins consistently in the supplied environment file.
- Set the trusted API host in both
SELF_HOST_TRUSTED_API_HOSTandALLOWED_HOSTS. A hostname missing from either allowlist is rejected even if DNS and TLS work. - Keep database credentials, signing material and registry access outside source control. Save the one-time self-host license securely before closing the provisioning screen.
- 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
- Requesting a deployment —
POST /api/v1/selfhost/request(CompanyAdmin+, gated behind theself_hostingfeature key, Enterprise-only) creates aSelfHostLicenserow (SH-XXXX-XXXX-XXXX-XXXXformat 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. - 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 provideddocker-compose.selfhost.yml. - Check-in — the running instance calls a public check-in endpoint
daily. This is a no-op unless
SelfHost:LicenseKeyis configured, so the same image is harmless running as PermitCore's own SaaS. - 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. - 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.
/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.
| Action | What happens | When 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.