On-Premises Server
A Consystence site server is the Site-mode build of the Consystence server — the same codebase as the cloud tenant runtime, published as a Site-baked artifact. It is the on-premises tier of the deployment model: it serves operators in the browser, holds the site’s device model and runtime state in Orleans grains, and reads the plant’s controllers through the edge runtime over gRPC, or directly. It is built for facilities with intermittent or no internet connectivity — a CHPP, a mine site, a water-treatment plant — where operator control must keep working whether or not the cloud is reachable.
This page covers what an on-prem site server actually is today, how to install and run it under systemd, and how it joins the cloud. For cloud-hosted deployments see Azure deployment; for the edge box see edge deployment.
What “on-prem” means today
Section titled “What “on-prem” means today”One codebase is published as two server artifacts, Cloud and Site. The mode is baked into each artifact at build time and is never read from configuration — a Site artifact cannot be flipped to Cloud at runtime. A site server:
- Runs in Site mode — the provisioning and org-management endpoints (
/api/provision/*and the like) are guarded off and return 404. The audited configuration and API-key admin surfaces (/api/admin/config*,/api/admin/api-keys) are served locally, so a site’s owners and admins who sign in with site PINs can manage configuration and API keys while offline, and the operator-initiated restore surfaces (/api/admin/orgs/{slug}/model-restoreand/api/admin/orgs/{slug}/tenant/restore) are reachable on a site server. - Serves one tenant, fixed when the server is activated or provisioned; a site is a path under it (
/sites/{code}), never a separate domain. Operators reach the site server at its own address on your network, for examplehttps://site.example.com: the hostname on its HTTPS certificate, or on the reverse proxy in front of it. It is not reached at the cloud{slug}.consystence.comhostname, which belongs to your cloud cell. See the org and site model. - Persists Orleans grain state to PostgreSQL 16 and serves operators over SignalR with the server-driven UI.
Single-silo Orleans is the on-prem default
Section titled “Single-silo Orleans is the on-prem default”An on-prem site server runs a single Orleans silo. That is the supported on-prem topology today.
- Clustering defaults to localhost membership — a single silo has no membership table to coordinate.
- The PostgreSQL ADO.NET provider backs grain storage and reminders — the durability layer for grain state and durable scheduled callbacks. It also provides opt-in multi-silo membership (
Clustering:Mode=AdoNet, with a mandatory per-siloClustering:AdvertisedIp) alongside SignalR silo-affinity placement. - Multi-silo clustering is the cloud’s production serving posture, not the on-prem default. The clustering and affinity code is shared across both artifacts, but an on-prem site’s serving posture for it — shared DataProtection keys, connection affinity under sustained load, NTP/clock-skew prerequisites — is still hardening. Run single-silo on-prem until that lands.
Prerequisites
Section titled “Prerequisites”- A Linux x64 host (physical or VM) on the site’s IT/OT-segmented network.
- PostgreSQL 16, reachable from the server host, with a dedicated database and login.
- A TLS certificate for the site server’s own hostname, served in-process (
SiteTls__*) or by a TLS-terminating reverse proxy.
Install from the Linux package
Section titled “Install from the Linux package”The site-server package you receive is a .deb package (consystence-site-server) or a tarball with an install.sh installer. Both lay down the same layout: the self-contained app at /usr/lib/consystence/site-server (no .NET runtime prerequisite), the consystence-site-server.service systemd unit, the consystence system user, and a config template at /etc/consystence/site-server.env.example.
sudo apt install ./consystence-site-server_<version>_amd64.deb # or sudo ./install.sh from the tarballsudo cp /etc/consystence/site-server.env.example /etc/consystence/site-server.envsudo chown root:consystence /etc/consystence/site-server.envsudo chmod 0640 /etc/consystence/site-server.env# fill in the database connection and ApiKeyPepper (see Configuration below), then:sudo systemctl enable --now consystence-site-serverjournalctl -u consystence-site-server -fRun under systemd
Section titled “Run under systemd”The package installs the consystence-site-server.service unit at /usr/lib/systemd/system/. It runs the server as the consystence user from /usr/lib/consystence/site-server. systemd creates /var/lib/consystence (owner-only) for the server’s state file, server-state.json (the write-once install id and activation state), and its Data Protection key ring (DataProtection__KeyPath), which keeps site sign-in, cookies and antiforgery tokens valid across restarts. The unit reads your settings from /etc/consystence/site-server.env, whose values override the unit’s defaults; keep secrets (the database password, the API-key pepper, certificate passwords) in that file, not in the unit.
Until HTTPS is configured, the server answers plain HTTP on http://localhost:8080, on this host only. A configuration refusal (for example an unverified database link) stops the service in the failed state instead of restarting it, and journalctl -u consystence-site-server says what to change.
Enable and start it:
sudo systemctl daemon-reloadsudo systemctl enable --now consystence-site-serversudo systemctl status consystence-site-serverjournalctl -u consystence-site-server -fConfiguration
Section titled “Configuration”The server’s bootstrap settings go in /etc/consystence/site-server.env as environment variables, with __ as the section separator (ConnectionStrings__ConsystenceDb sets ConnectionStrings:ConsystenceDb); the packaged site-server.env.example documents each one. Environment variables are restricted to a bootstrap allow-list: the database connection, the listen address and environment, Kestrel, the edge listener, clustering identity, the state and Data Protection paths, the in-process HTTPS settings (SiteTls__*, except a PFX certificate password), Server__PublicUrl / Server__StateFilePath, and the API-key pepper. Everything else comes from appsettings.json, appsettings.Local.json beside the server, or the server’s audited configuration store (encrypted secret rows, reloaded cluster-wide); a non-bootstrap setting supplied as an environment variable is not used. Deployment mode is not a setting — the published site artifact is Site-mode regardless of configuration, and a Server:Mode / Server__Mode entry is inert.
Database and API-key pepper
Section titled “Database and API-key pepper”# Database on this host (needs no TLS):ConnectionStrings__ConsystenceDb=Host=localhost;Database=consystence;Username=consystence;Password=CHANGE_ME# Database on another machine (verified TLS is required, or the server will not start):# ConnectionStrings__ConsystenceDb=Host=db.example.internal;Database=consystence;Username=consystence;Password=CHANGE_ME;SSL Mode=VerifyFull;Root Certificate=/etc/consystence/db-ca.pem;Require Auth=ScramSHA256
# Required: at least 32 characters, generated once (see below)ApiKeyPepper=<generated secret>
# Recommended: protect the Data Protection key ring with a certificate# DataProtection__CertPath=/etc/consystence/dp-cert.pfx# DataProtection__CertPassword=CHANGE_MEConnectionStrings:ConsystenceDbis the server’s one database: application, configuration, cluster state and historian. The server creates its schemas in it at every start, so create the database owned by the server’s role (CREATE DATABASE consystence OWNER consystence;). A database on another machine must use verified TLS (SSL Mode=VerifyFullwith the issuing CA’s certificate at/etc/consystence/db-ca.pem), and the database server’spg_hba.confshould allow this server TLS withscram-sha-256only. Older two-database layouts (ConnectionStrings:Postgres+ConnectionStrings:OrleansStorage) run without the configuration store.ApiKeyPepperis required: the server will not start without it. Generate it once, as one line (for exampleopenssl rand -base64 64 | tr -d '\n'), and put it in place of the placeholder. Keep the same value for the life of the install, and a copy with your database backups: a changed pepper invalidates every API key already issued.
To serve browsers and edges, put a PEM certificate (full chain) for the site server’s own hostname and its unencrypted key in /etc/consystence/tls (directory 0750 and key 0640, group consystence), and enable in-process HTTPS:
SiteTls__Enabled=trueSiteTls__HttpsPort=443SiteTls__CertPath=/etc/consystence/tls/site.crtSiteTls__CertKeyPath=/etc/consystence/tls/site.keyThe unit grants CAP_NET_BIND_SERVICE, so the server can bind :443 without root. Plain HTTP stays on localhost:8080 for tools on this host, such as health probes. Alternatively, run a reverse proxy on this host that terminates TLS for the site server’s own hostname and forwards to http://localhost:8080, and set Server__PublicUrl to the address people sign in at (for example https://site.example.com). Do not widen plain HTTP to the network: it would carry sign-in PINs and session cookies unencrypted.
Edge listener
Section titled “Edge listener”The site server serves the edge runtime on one port of its own, and on no port until you set it. With in-process HTTPS enabled (the SiteTls__* settings above), add EdgeListener__Port=5001 (a port other than the HTTPS and HTTP ports) to /etc/consystence/site-server.env and restart; the server then serves the edge gateway over HTTPS on that port only. Allow only your edge hosts to reach that port. Point each edge’s Upstream:ServerUrl at it (for example https://site-server.example:5001); the edge must trust the site server’s certificate.
Cloud link
Section titled “Cloud link”Server:CloudUrl is the base URL of your tenant on its cloud cell ({slug}.consystence.com) — not the account console. The site posts activation to {CloudUrl}/api/provision/site-activation and syncs historian and model data to the same cell; the cell mediates with the account control plane, which serves neither surface itself. Set it in appsettings.Local.json beside the server (/usr/lib/consystence/site-server/appsettings.Local.json):
{ "Server": { "CloudUrl": "https://acme.consystence.com" }}Once you can sign in as a site admin, the audited configuration store (csy config set Server:CloudUrl …) works too. A Server__CloudUrl environment variable is not accepted, and the server will not start while one is set with no other source.
Bringing the server into service
Section titled “Bringing the server into service”A fresh install comes into service in one of two ways. Cloud-linked: the server starts in setup mode and waits for a one-time activation code issued from the account console. POST /api/setup/site-activation with the code and the code of the site it was issued for ({ code, siteCode }, plus an optional siteName) relays them to your cloud cell and applies the returned bundle, establishing the server’s identity. Without siteCode, no site is created or joined and no site→cloud sync key is issued. Standalone: a server that will not link to the cloud is provisioned from a tenant bootstrap file (tenant.yaml, named by Tenant:BootstrapPath in appsettings.Local.json). Once /api/setup/status reports it Active, create its first administrator on the host with sudo consystence-site-admin seed-first-administrator --email <address> --display-name <name>. For the credentials clients present, see Authentication; see offline operation below for what works without the cloud.
Joining the cloud
Section titled “Joining the cloud”A site server is not enrolled as a cell. It runs its own Orleans cluster (single-silo by default) and joins the cloud as a synchronising peer:
- The account control plane issues a one-time activation code for a site the tenant has declared, and the code is bound to that site. The account holds the organisation’s identity, entitlement, and elevated roles (Owner/Admin). It does not hold the plant model: devices, scenes and electrical topology — the
tenant.yamlsurface — are site-authored and applied to both the site server and its cloud cell. Device types are not part of that surface: they come from the platform’s global catalogue of first-party types. - The site server redeems that code through its cloud cell: the cell checks the code with the account control plane, creates or joins the site the code was issued for (a code bound to one site never creates or joins another), mints the site→cloud sync key (bound to
{orgSlug}/{siteCode}), and returns the activation bundle. The sync key is an output of activation, never the credential the site presents to enrol. The site thereafter synchronises upward through a dedicated sync surface — historian samples always, and site-model changes once enabled (model sync is opt-in during beta:Sync:ModelSyncEnabled, default off). In the other direction, an operator can restore selected model scopes from the cloud’s custody copy back onto the site — restore is always operator-initiated and site-pulled, shows a full preview (including removals) and requires exact confirmation before anything is applied; the cloud never pushes model changes down automatically. There are no cross-cluster Orleans calls: the site cluster and the cloud cluster are hard-isolated by design and communicate only through the sync surface.
flowchart TB
subgraph cloud["Cloud (public beta)"]
acct["Account control plane<br/>account.consystence.com<br/>activation codes + tenant provisioning"]
cell["Cloud cell<br/>{slug}.consystence.com"]
end
subgraph site["On-premises site server — Site-mode binary"]
silo["Orleans silo<br/>(single-silo)"]
pg[("PostgreSQL 16<br/>grain storage + reminders")]
silo --- pg
end
cell -->|checks activation code| acct
ops["Operators (browser)"] -->|SignalR| silo
silo -->|gRPC| edge["Edge runtime"]
silo <-->|"activation → sync key<br/>historian sync + opt-in model sync"| cell
This is distinct from the cell-mediated provisioning of cloud-hosted tenants — see Tenant provisioning and cell topology. For this server’s side of activation, see Site activation.
Licensing
Section titled “Licensing”Licences are issued by the account control plane and cell-mediated; entitlement is bound to provisioned identity, not hardware. An organisation carries a tier (free by default, or a paid Integrator or Enterprise tier arranged with our team; see plans) plus per-seat SKU pools that license its installs. A site server can hold an account-signed (ECDSA) offline licence, delivered at activation and verified locally, so it needs no cloud connection to check its licence.
Licence enforcement is degrade-not-kill and, critically, never gates operator control writes. After a 30-day grace period, a lapsed licence can only block expansion: adding new sites and device instances. Existing devices, scan loops and operator commands are unaffected. See licensing for the full model.
Offline and degraded operation
Section titled “Offline and degraded operation”A site server is designed to keep operating when the cloud is unreachable:
- Authentication stays local: operators sign in with their site PINs, which the site server checks itself with no cloud connection, and tenant API keys (
csk_prefix) keep working. Site sign-in credentials cannot authenticate to the hosted cloud service. See the auth model. - Field data continues to flow: the edge runtime buffers store-and-forward over SQLite and replays to the site server on reconnect.
- Licensing is checked locally and needs no cloud connection; a licence problem never blocks control.
Verify the install
Section titled “Verify the install”# Service is upsystemctl is-active consystence-site-server.service
# Local health probe (through the reverse proxy or Kestrel directly)curl -s http://127.0.0.1:8080/api/setup/statusThe setup/status response reports the server mode, version and activation state — confirm it shows Site mode and the expected tenant identity. Endpoint shapes are in the API reference.
Next steps
Section titled “Next steps”- Edge deployment — the edge runtime that reads controllers and feeds this server.
- Azure deployment — the cloud-hosted alternative.
- Architecture overview — where the site server sits in the layered model.
- PLC communication — how field data reaches the site server.