Self-hosted & offline
One file. Your machine.
No route out required.
The web UI is embedded in the binary, so an install is a file you copy and run. Licences are verified locally against a pinned public key — an air-gapped panel works exactly like a connected one, for as long as the licence is valid.
Install
Pick the shape that fits your estate
Every one of these is the same server with the same features. Only the wrapper differs.
Windows — per-user .msi
Installs into %LOCALAPPDATA%\Programs\Control Panel, so it needs no administrator
rights. It sits in the notification area while it runs, with Open and Quit.
control-panel-<version>-x64.msi
macOS — .dmg
Carries Control Panel.app, which runs as a background agent, lives in the menu bar and opens
your browser. config/ and logs/ stay beside the app.
open control-panel-<version>-arm64.dmg
Linux — binary or systemd
A single static executable, or the provided unit file for a service that starts at boot. Cross-compiled for amd64 and arm64.
./control-panel -adduser admin -role admin
Docker & Kubernetes
A published image, a docker-compose.yml, and a Helm chart with persistence for
config/ and logs/, optional ingress and secret-backed tokens.
helm install cp deploy/helm/control-panel
docker compose build # create an admin (prints a one-time password), then start docker compose run --rm panel -adduser admin -role admin docker compose up -d # config/ and logs/ are bind-mounted, so users, tokens, # known_hosts and history survive a rebuild
# the UI is embedded; this file is the whole product ./control-panel -adduser admin -role admin CP_MODE=offline ./control-panel # listening on http://localhost:8099 — it opens your browser # serve HTTPS directly if you prefer: ./control-panel -tls-cert cert.pem -tls-key key.pem
Download Control Panel for Windows (v2.1.1)
The per-user Windows installer, a bare .msi: it installs into
%LOCALAPPDATA% and needs no administrator rights.
Build v2.1.1
SHA-256 fb1e04cc14a1e20adf49778f09049d0ae069aaa58685725fe38290db5279d2bd
Older builds, macOS and checksums: all versions.
# the hash must match the SHA-256 printed beside the button Get-FileHash .\control-panel-v2.1.1.msi -Algorithm SHA256 msiexec /i .\control-panel-v2.1.1.msi # then launch it from the Start menu and paste your licence key
CP_MODE=offline is stated, never inferred. Self-hosted mode has to be asked for by
name. A misconfigured multi-tenant deployment must not quietly become a single-tenant one with the
authentication turned off — so the panel refuses to start rather than guess.Licensing
Offline licence verification: locally, not remotely
The panel holds the issuer's Ed25519 public key, compiled into the binary. It checks the signature, the expiry, the product and the limits itself. There is no call to us in the path of you using the software you paid for.
- Install, launch, paste the key. The panel opens the activation screen directly — no redirect to hunt for.
- Soft licences are not tied to hardware: they survive re-imaging and move between machines.
- Machine-bound licences add activation slots with a ceiling, for when a contract needs one.
- The token lives in
config/licence.token— a backup that capturesconfig/captures the licence. - A wound-back clock does not revive an expired licence — the highest reading seen is remembered.
LIC-7QF4-2M8X-JR91-0KDEAir-gapped activation
The activation screen shows a machine ID — a hash of the machine's identity that carries no hostname and no MAC address. Send it to us; we issue a token already bound to that machine, which then verifies with no network at all.
Containers & clusters
There is no stable hardware identity in a pod, so set LICENCE_MACHINE_ID explicitly — a
StatefulSet pod's own identity works well. Without it a rescheduled pod would burn an activation slot
every time.
Renewals & expiry
An expired licence stops the console and shows the activation screen. Nothing is deleted:
config/, deployment history, monitor history and the audit log stay on disk, and a renewed
key restores everything immediately.
Running it
What lives where, and what you have to look after
State on disk
config/— users, tokens, remembered sessions,known_hosts, yourenvironments.jsonandservices.json, and the licence.logs/— the audit log (rotated), monitor history, deploy history.- That is the whole backup. Copy those two directories and you have copied the install.
Nothing to prepare first
- On a brand-new host — including an empty mounted volume — the server writes its own
config.json,services.json,environments.jsonandcustom_actions.json, seeded for the OS it is actually running on. - Existing files are never touched.
- Embedded scripts are extracted on first start, and a new release upgrades the ones still as the last release wrote them — a script you edited is left alone.
Upgrading in place
- A chip appears in the header when a newer release is published, and an Updates page in Settings shows the installed version, the release notes and the channel. Skip this version hides the chip for one release.
- On Windows and macOS an administrator can install it from there. The published SHA-256 is verified before anything runs, and a download whose hash does not match is deleted rather than left on disk. Nothing installs on its own.
- On Linux and in containers the panel reports the release and links the download but does not replace itself — there the running version belongs to your compose file, unit or package manager.
- Release information comes from the same licence server the install already talks to, so there is no second service to configure.
CP_UPDATES=0switches checking off entirely.
Version and channel
- Everyone who can sign in sees which version is running. Only an administrator can check, install or change the settings — installing restarts the panel for every session connected to it.
- Checks, installs and settings changes are all audited.
- Automatic checking runs twice a day and can be turned off, or pointed at a different release channel, per install.
| Variable | What it does |
|---|---|
CP_MODE=offline | Selects the single-team, no-database install. Required for self-hosting. |
CP_ADDR | Listen address, default localhost:8099. Binding beyond loopback requires authentication. |
CP_TLS_CERT / CP_TLS_KEY | Serve HTTPS directly instead of behind a proxy. |
CP_TRUSTED_PROXIES | Whose X-Forwarded-For to believe. Set this whenever the panel runs behind a proxy, or every request looks like it came from the proxy. |
CP_METRICS_TOKEN | Bearer token for Prometheus, which cannot log in. |
CP_CI_WEBHOOK_SECRET | Shared secret for the CI webhook. Unset means the webhook is disabled. |
CP_AUDIT_LOG | Audit log path, default logs/audit.log. Rotation size and retention are configurable. |
CP_SSH_STRICT=1 | Disable trust-on-first-use; unknown SSH hosts must be pre-listed. |
CP_SESSION_IDLE_MINUTES | Session timeouts, default 30 minutes idle and 12 hours absolute. |
CP_UPDATES=0 | Switch update checking off entirely, for a deployment whose policy forbids the outbound call. |
CP_UPDATE_INTERVAL | How often to check, as a Go duration (6h). Default 12 hours, minimum a minute. |
The full reference, including hardening and packaging, is in the documentation.
Who self-hosts
Three reasons people choose the licence
The data cannot leave
Regulated industries, government, defence suppliers, and anyone whose hosts are not reachable from the public internet in the first place. The panel runs inside the boundary and never calls out.
The panel is the desktop tool
An engineer who wants the console on their own laptop: host shell, local containers, disk cleanup, phone import. All of that only exists where the panel owns the machine.
Upgrades on your calendar
Change windows, frozen releases, a version pinned for an audit. You take the update when it suits you, not when it ships.
Self-hosting questions
What people ask before installing
Do I need a Windows machine?
Only to manage Windows targets — those use cmd.exe, PowerShell, WinRM
and RDP. Docker, Linux and Kubernetes environments are Linux-native and run from the container image, a
Helm chart or a systemd unit with no Windows host anywhere.
Does anything need to be installed on the hosts I manage?
No agent, ever. The panel reaches out over SSH or WinRM and drives Kubernetes with
kubectl. The only requirement is that it can reach them and has credentials.
How do upgrades work?
Replace the binary, or pull the new image, and restart. config/ and
logs/ are untouched by an upgrade, and existing configuration files are never rewritten.
Can the panel run behind our reverse proxy / SSO?
Yes. Set CP_TRUSTED_ORIGINS and CP_TRUSTED_PROXIES so
origin checks and the audit trail see the real client, and configure the OIDC block for single sign-on
against Okta, Entra ID, Google or Keycloak. Local users remain as a fallback for the air-gapped case.
What does the licence server ever get told?
On a connected install: an activation request carrying the machine ID hash, and a fetch of the signed revocation list at startup and hourly. Nothing about your environments, hosts, services or users is ever sent — the panel has no channel for that at all. Build without a licence server configured and it makes no outbound call whatsoever.
Can we evaluate it before buying?
Install it and press Start my 30 free days on the activation screen. One click asks our licence server for a trial for that machine and installs what comes back — no card, no sales call, and it is the same build customers get. If the machine has no route out, or you would rather we issued one by hand, ask us and we will send a key to paste in instead.
Install it this afternoon
Download it, press Start my 30 free days, and you are in — full product, no card. Licences start at $149 per install per year after that.
Air-gapped or a site licence? Talk to us — we issue against a machine ID by email.