Skip to content
Cenovel

Security architecture

Cenovel runs on your own appliance and needs no internet access. This page is for the security reviewer and the network engineer: what is exposed, how people sign in, how secrets, backups and the audit trail are protected, and how releases are signed and installed.

Updated October 5, 2026

At a glance

Where it runs
One Debian 13 (amd64) appliance VM on your VMware vSphere, delivered as an OVA. Nothing is hosted by Cenovel.
Network
Staff reach it over HTTPS. It connects out only to the devices it reads and the services you configure. No telemetry, no update check, no license server.
Sign-in
Active Directory over LDAPS (the default) or StartTLS, optional authenticator codes, Microsoft Entra ID, and a local recovery account.
Secrets
Stored secrets are sealed with AES-256-GCM under a rotatable settings key that never leaves the appliance.
Releases
Bundles are signed with Ed25519. Updates are signed with a separate update key and installed by a root updater the web app cannot control.
Devices
Read only, by construction: Cenovel cannot send a command that changes a device. See Read-only by construction.
Your data
Stays on your appliance. See Data handling, and for districts, No student data.

Who secures what

Cenovel is software your organization installs and runs. It is reachable only on your network, and Cenovel the company has no access to it, or to your data, unless you grant access for a support case.

  • You secure the environment: the hosts and the appliance VM, network placement and firewall rules, remote access, your directory and MFA policies, who holds which Cenovel role, device credentials, backups and the backup keyring, and installing security releases promptly.
  • Cenovel secures the software: secure development with dependency and secret scanning, a software bill of materials with every release, signed releases and site collectors, timely security fixes, and the hardening guidance in the documentation.

Deployment and network exposure

The appliance has one network adapter, on your management VLAN. Keep it off the internet.

Inbound

PortServesNotes
TCP 443The console and its API, over HTTPSFrom staff workstations only. Served by Caddy.
TCP 80A redirect to HTTPSCaddy’s default. No content is served over plain HTTP.
TCP 22SSH for the appliance’s operator accountRoot cannot sign in over SSH. Limit it to administrators’ networks.

The application listens on 127.0.0.1:3100 only, behind Caddy. Its database accounts are created for 127.0.0.1 only. The appliance does not configure a host firewall, so restrict ports 22 and 443 at your network firewall.

Outbound

ToPortWhen
Managed switches, stacks and access pointsSSH TCP 22, SNMPv3 UDP 161Always, for the devices you add
Server management controllers (Redfish)TCP 443If you add servers
Your domain controllersLDAPS TCP 636, or StartTLS TCP 389If directory sign-in is on
login.microsoftonline.com and Microsoft GraphTCP 443If Microsoft sign-in is on
Your Juniper Mist region’s API host, or ExtremeCloud IQTCP 443If connected
Your mail serverTCP 587 (STARTTLS) or 465If email is set up
Your webhook endpointsAs configuredIf set up
Your off-box backup hostSSH or SFTP, TCP 22 by defaultIf set up

Nothing else. The appliance sends no telemetry, never checks for updates and never contacts Cenovel. The license is a signed file checked on the appliance (Ed25519), offline. For networks the appliance can’t route to, a site collector at the site reads devices on the management subnets you allow it.

Encryption in transit

The console
HTTPS only, served by Caddy with your district’s certificate or the appliance’s own certificate authority. TLS versions and ciphers are Caddy’s defaults (TLS 1.2 and 1.3). Caddy sends Strict-Transport-Security: max-age=31536000.
Browser protections
A Content Security Policy that allows scripts only from the console itself, with frame-ancestors 'none' and object-src 'none'; X-Frame-Options: DENY; X-Content-Type-Options: nosniff; and Cache-Control: private, no-store on API responses.
Directory sign-in
LDAPS by default, or StartTLS. Plain LDAP is refused. TLS 1.2 or later, the certificate must chain to the CA bundle you give Cenovel (which replaces the system store), and the server name is checked.
Microsoft Entra ID
HTTPS to Microsoft’s published endpoints. The signing keys must come from the issuer’s own HTTPS origin.
Devices
SNMPv3 with authentication and privacy (authPriv), SSH, and HTTPS for Redfish, Mist and ExtremeCloud IQ.
Email
STARTTLS on 587 or TLS on 465. Plain SMTP is refused unless an administrator explicitly allows it.
Off-box backups
rsync over SSH or SFTP, with the backup host’s key pinned in a known-hosts file.

Sign-in and sessions

The recovery account

First boot creates a local administrator and a random recovery passphrase, shown once on the appliance console until you store it and acknowledge it. Only its hash is kept: scrypt with N=16384, r=8, p=1 and a 16-byte random salt. It always works, even when the directory is down, and it is the only account that can grant or remove Administrator EX.

Active Directory

  • Passwords are checked by a bind to the directory and are never stored or logged.
  • Groups map to access tiers. Nested membership counts, and the highest tier wins. Someone in no mapped group cannot sign in.
  • While a directory session is in use, its membership is read again every 5 minutes. A person removed from their groups, or disabled, loses access within about 5 minutes, and at most 15.
  • Optional authenticator codes: TOTP (RFC 6238, HMAC-SHA1, 30-second steps, 6 digits), each code usable once. The authenticator secret is sealed with the settings key. Ten recovery codes are stored as SHA-256 digests.
  • After 5 failed attempts in 15 minutes, Cenovel locks that account for a growing interval.

Microsoft Entra ID

Authorization code flow with PKCE (S256), a random state and nonce. The ID token’s signature is checked against the tenant’s published keys, with its issuer, audience, expiry and tenant id. The tenants common, organizations and consumers are refused. Groups map to tiers as with the directory.

Sessions

ControlValue
Session token256 random bits. The server stores only its SHA-256 hash.
CookieHttpOnly, Secure, SameSite=Strict (Lax after a Microsoft sign-in, which returns from Microsoft)
Longest session8 hours. A session whose tier comes from directory or Entra groups needs its membership confirmed at least every 15 minutes.
Inactivity sign-out30 minutes by default; set from 5 to 1,440 in Settings (“Sign out after inactivity”). Background refreshes don’t count as activity.
WarningTwo minutes before an inactivity sign-out, with “Stay signed in”.
Cross-site requestsChanges must come from the console’s own origin (Origin and Referer are checked), on top of the SameSite cookie.
GuessingAfter 5 failed sign-ins from one address, each further attempt waits longer, starting at 30 seconds. Repeated failures on one account are recorded in the audit log.

Signing in again

Installing an update asks the person to prove it is them again, so a stolen session is not enough: the recovery passphrase, a fresh directory password check, or a Microsoft sign-in within the last 5 minutes. After 5 wrong attempts it pauses for 5 minutes.

Access tiers, page rules and department scope

Every rule below is enforced by the server, on each request. The browser only hides what the server would refuse.

TierCan
Administrator EXEverything, across departments, including settings, updates, page rules and the district-wide audit log
AdministratorChange records and structure in their own department; assign Operator, Auditor or Monitor there; read its audit log
OperatorChange records; export
AuditorRead and export, including the district-wide audit log; change nothing
MonitorRead; no exports
  • Page rules set six actions per page: view, download, create, edit, delete and design. A department rule or a personal rule can allow or block an action, within what the tier permits. A refused action returns 403 and is recorded as “Page permission denied”.
  • Department scope limits whose records a person can change; a change to another department’s equipment is refused.
  • API tokens (Authorization: Bearer syn_…) carry 256 random bits and are stored as SHA-256 hashes; the token is shown once. A token’s scopes can never exceed its creator’s own rights, its tier is at most Administrator, and it expires after 90 days by default (730 at most).

Secrets at rest

Secrets the console stores in its database are sealed with the settings key: AES-256-GCM with a random 96-bit nonce and a 128-bit tag. The authenticated data binds each value to its key id and to the setting it belongs to, so a sealed value can’t be moved to another field.

Sealed with it
SNMPv3 authentication and privacy keys; SSH passwords, private keys and enable passwords; Redfish passwords; Juniper Mist and ExtremeCloud IQ tokens and the Mist webhook secret; site collector keys; the SMTP password and webhook secrets; authenticator secrets.
Where the key lives
In /etc/cenovel/credentials/settings-key on the appliance, readable by root only (directory 0700, file 0600), and handed to the console and worker as a systemd credential. First boot generates it with every other per-appliance secret: database passwords, the recovery passphrase, the backup keys and the metrics token.
Rotation
cenovelctl settings-key rotate adds a new key and makes it active; reseal re-encrypts every stored secret under it; retire KEY_ID removes an old key and refuses while anything still uses it. status shows which keys are in use.
Never stored
Directory passwords, Microsoft passwords and API tokens in clear text.

For encryption of the disks themselves, use vSphere VM encryption.

Read-only by construction

Reading never changes your network. That is enforced in Cenovel’s own code, at the single point where each protocol leaves the console or a site collector, and does not rest on the credentials you give it. The few changes Cenovel can make (blinking a locator light, and changing switch ports once you turn that on) are separate named paths, each behind its own switch, audited and checked before and after; none of them passes through the read path.

SSH
Every command line passes one send point. Before anything connects, each line is checked against the read-only show commands Cenovel sends for that platform (Cisco IOS and IOS XE, Cisco access points, Junos, Switch Engine, Fabric Engine and HiveOS), and against a deny list that wins over everything: configure, set, delete, edit, commit, rollback, load, request, write, copy, erase, reload, reboot, shutdown, no, clear, debug, shells, save, enable, file operations, ping and traceroute, chained commands, and pipes or redirects to a file. The deny list matches any capitalization and spacing, and the abbreviations the device command lines accept, such as conf t and wr. A read that is simply not on the list is refused without being sent.
SNMP
Cenovel loads its SNMP library with SET, TRAP and INFORM removed, and only GET, GETNEXT and GETBULK requests can reach the network.
Controllers and servers
The clients for Redfish, Juniper Mist and ExtremeCloud IQ send GET requests and nothing else. Redfish is read with HTTP Basic authentication on each request, so Cenovel doesn’t open or close sessions on a server’s controller.
Nothing relaxes it
The command lists and the deny list are part of the signed release. No setting, environment variable or stored record can widen them.

If Cenovel ever tries

  • Nothing is sent. The command or request is refused before a byte of it reaches the device.
  • All device communication stops: polling, finds, server and controller reads on the console, and every site collector. The console itself stays up, with a banner on every page, so people can see what happened.
  • It is recorded and announced. An entry in the hash-chained audit log holds the command (with any secret removed), the device and where in Cenovel the attempt came from, and a critical alert goes to every enabled notification channel.
  • It survives a restart. The stop is kept in the database and on each collector’s disk.
  • Only an Administrator EX can resume it, after giving a reason and signing in again as for an update. The release is in the audit log.

Give Cenovel read-only device credentials unless you turn on switch changes: an SNMPv3 user with a read-only view, an SSH account limited to show commands (privilege level 1 on Cisco, a read-only login class on Junos), and read-only controller tokens. They are the guarantee that doesn’t depend on Cenovel’s code.

Device credentials

SNMP
SNMPv3 authPriv only; v1 and v2c communities are refused. Authentication SHA-224, SHA-256, SHA-384 or SHA-512; privacy AES-128. Cenovel can’t send SNMP set requests (read-only by construction).
SSH
Each device’s host key is pinned by its SHA-256 fingerprint, recorded by an administrator. An unknown or changed key is refused, and reported differently from a device that doesn’t answer. Commands come from a fixed list of read-only show commands for each platform, behind a hard-coded deny list; a command that could change a device is refused before it is sent and stops all device communication (the tripwire).
Redfish
Read with GET only. Each controller’s certificate is pinned, checked against a CA you supply, or left unchecked only by an administrator’s audited override; a changed certificate is reported, and no credential is sent until a certificate is trusted.
Cloud controllers
Read-only tokens for Juniper Mist and ExtremeCloud IQ. Cenovel sends them GET requests only.
Our advice
Give Cenovel its own read-only accounts on your devices and controllers, so its credentials can do nothing else, whatever Cenovel’s code does.

The audit trail

Every change and sensitive action is recorded with who, when, from which address, and the values before and after. The log is a hash chain: each entry stores the SHA-256 of its own fields together with the previous entry’s hash, so a changed, removed or reordered entry breaks the chain at that point.

  • Check it on the console’s audit page, which shows the chain’s status, or through the API (GET /api/audit/chain).
  • Export it as an evidence file with a SHA-256 digest of its contents. The export refuses to run while the chain does not verify.
  • What it proves: the chain is unkeyed, so it detects accidental corruption and edits made outside the console, not a privileged attacker who rewrites the whole database. Keep exported evidence off the appliance.
  • Records with funding, E-Rate, disposal, replacement or RMA history are never removed for good.

Backups and the restore drill

What a set holds
The database dump, the configuration, the release identity and every application store, with a manifest of each file’s SHA-256 and a completion marker written last.
Encryption
Each file is encrypted with AES-256-GCM under the active key of the backup keyring, with its header (key id, set id, time) authenticated. Without the keyring a set can’t be read. Keep an escrow copy of the keyring away from the backups.
Schedule
Nightly at 01:30. Keeps 7 daily, 4 weekly and 2 safety sets by default.
Off-box copy
rsync over SSH or SFTP to your host, with its host key pinned. Each copy is read back and compared by digest before it is recorded.
Restore drill
cenovelctl restore drill restores the newest set into scratch space and proves a console starts on it. Run it monthly.
Restore
Verifies the set, takes a safety set of what it replaces, restores, then starts the console behind the readiness check.

Software supply chain

Signed releases

Releases are signed with Ed25519. A key’s id is the SHA-256 of its public key. The private keys are kept offline and never enter the source repository, a bundle or an appliance.

Signed fileKeyChecked by
Release bundle (.cenovel) for a new installRelease keyThe installer, against /etc/cenovel/trust
Update bundleUpdate keyThe root updater, against /etc/cenovel/trust/update.pem only
RELEASE.json (version, schema range, SBOM digest), INTEGRITY.json, site collector releasesRelease keyThe installer, the console and each collector
SHA256SUMS for each releaseRelease keyYou, as below

A bundle’s signed manifest lists the SHA-256 and size of every file and of the whole payload; a bundle that differs by one byte is refused. Keeping the update key separate means a key that can install updates can’t also sign collector releases or the integrity manifest.

Verify a download

Each release is published with SHA256SUMS, listing the OVA, the bundle and release.pem, and SHA256SUMS.sig, a detached Ed25519 signature over it. With OpenSSL 3.0 or later:

openssl pkeyutl -verify -rawin -pubin -inkey release.pem -in SHA256SUMS -sigfile SHA256SUMS.sig
sha256sum -c SHA256SUMS --ignore-missing

Compare the release key’s fingerprint with the one Cenovel gives you separately from the download:

openssl pkey -pubin -in release.pem -outform DER | sha256sum

On the appliance, cenovelctl release verify <bundle> checks a bundle. See Verify a release.

What runs is what was signed

Releases are installed read-only. cenovelctl release verify-current checks every installed file against the signed manifest, and runs before the console, the worker, backups and restores start, so a changed file stops them. INTEGRITY.json adds a signed SHA-256 list of the application’s own code, which the console checks at startup and reports if anything differs.

Software bill of materials

Every release carries sbom.cdx.json, a CycloneDX SBOM of the runtime packages of the console and the site collector, built from the lockfiles. Its SHA-256 is signed into RELEASE.json.

Dependency and secret scanning

The project’s checks run npm audit on the console’s and the collector’s runtime dependencies and fails on any high or critical advisory. An exception needs a written reason and expires within 180 days; there are none today. A secret scan checks every tracked file.

Reproducible builds

Bundles are built with a fixed timestamp from the release commit (SOURCE_DATE_EPOCH), sorted entries, and normalized owners and file modes. A build check rebuilds the bundle from its prepared tree and requires an identical manifest. The prepared tree itself (installed dependencies and the built web client) is kept with the build rather than reproduced from source. The build records the SHA-256 of the Node.js 24 runtime it packages, and of each lockfile, in the release’s provenance.

Updates

Cenovel never downloads or installs anything on its own. An Administrator EX uploads a signed bundle in Settings › System › Updates and installs it after signing in again; or root runs cenovelctl upgrade apply on the appliance console. Both take the same path.

  • The web app never gets root. The console runs as an unprivileged user with no capabilities and no way to raise them. Its only power is to place the bundle and a request in a spool folder. A systemd path unit starts the root updater, which trusts nothing the console says.
  • Root checks it again. It opens the request and the bundle without following links, requires plain files written by the console, refuses a request that is over 15 minutes old or was used before, copies the bundle into its own folder, and from then on reads only that copy. It requires the update key’s signature, a release newer than the one running (a downgrade is a restore, never an update), a fit with the database version, and room on the disks.
  • Tried before anything stops. The release is checked against a copy of the database in a disposable schema while the console keeps serving. Then the console stops, a backup is taken and verified, the release switches and the database is updated.
  • It goes back on its own. If the new release doesn’t pass its readiness check, the appliance returns to the previous release, or restores the backup it took after the console stopped, so nothing is lost. A power cut mid-update is resolved at the next boot. While the console is down, a maintenance page shows the steps.
  • It is recorded. The audit log records the upload, the request with its reason and the outcome; the updater’s own journal keeps a record the console can’t change.

Upgrades and rollback

The appliance image

Base
Debian 13 (amd64) with only what Cenovel needs, installed without recommended extras: MariaDB, Caddy, OpenSSH, open-vm-tools, rsync and the Node 24 runtime inside the release.
Four disks
The system and releases; the database; the data; and local backup staging. Each is its own filesystem, mounted by UUID, so a full data disk can’t fill the system disk. A missing disk stops the boot rather than letting it start on an empty folder.
Nothing secret ships
The image build refuses to seal an image that holds credentials or has run first boot, and removes the SSH host keys, machine id, logs and build history. Each appliance makes its own on first boot.
Operator account
cenovel-admin ships locked. First boot gives it a random password shown on the VM console, which must be changed at the first sign-in. Root never signs in over SSH.
Services
The console, worker, migrator, discovery parser and collector each run as their own system user under systemd sandboxing: no new privileges, an empty capability set, a read-only system, and private temporary files and devices. The console, worker and discovery parser also run under a system-call filter, and the discovery parser has no network at all.
Operating system updates
Not installed automatically. Debian security updates are part of the district’s environment.

Data handling

What Cenovel holds, what it never holds, how long it keeps each kind of record, and how a person is erased, are on Data handling. For school districts: No student data.

Accessibility

For procurement: what we test, what we know is missing, and how to ask for the conformance report are on Accessibility.

Vulnerabilities and advisories

Write to [email protected]. A person reads every report and replies within 3 business days. Fixed vulnerabilities are published as security advisories with their CVSS score, affected and fixed releases, and what to do, and the contacts each district names for security notices are emailed. The previous major version gets security fixes for 12 months after the next one ships. The contact is also in security.txt.

Questionnaires and agreements

We answer security questionnaires, and we sign district data privacy agreements. Ask on the first call (book a demo), or write to [email protected].

See also: Security & data in the documentation · Incident and breach response · Directory sign-in