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
| Port | Serves | Notes |
|---|---|---|
| TCP 443 | The console and its API, over HTTPS | From staff workstations only. Served by Caddy. |
| TCP 80 | A redirect to HTTPS | Caddy’s default. No content is served over plain HTTP. |
| TCP 22 | SSH for the appliance’s operator account | Root 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
| To | Port | When |
|---|---|---|
| Managed switches, stacks and access points | SSH TCP 22, SNMPv3 UDP 161 | Always, for the devices you add |
| Server management controllers (Redfish) | TCP 443 | If you add servers |
| Your domain controllers | LDAPS TCP 636, or StartTLS TCP 389 | If directory sign-in is on |
login.microsoftonline.com and Microsoft Graph | TCP 443 | If Microsoft sign-in is on |
| Your Juniper Mist region’s API host, or ExtremeCloud IQ | TCP 443 | If connected |
| Your mail server | TCP 587 (STARTTLS) or 465 | If email is set up |
| Your webhook endpoints | As configured | If set up |
| Your off-box backup host | SSH or SFTP, TCP 22 by default | If 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'andobject-src 'none';X-Frame-Options: DENY;X-Content-Type-Options: nosniff; andCache-Control: private, no-storeon 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.
- 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
| Control | Value |
|---|---|
| Session token | 256 random bits. The server stores only its SHA-256 hash. |
| Cookie | HttpOnly, Secure, SameSite=Strict (Lax after a Microsoft sign-in, which returns from Microsoft) |
| Longest session | 8 hours. A session whose tier comes from directory or Entra groups needs its membership confirmed at least every 15 minutes. |
| Inactivity sign-out | 30 minutes by default; set from 5 to 1,440 in Settings (“Sign out after inactivity”). Background refreshes don’t count as activity. |
| Warning | Two minutes before an inactivity sign-out, with “Stay signed in”. |
| Cross-site requests | Changes must come from the console’s own origin (Origin and Referer are checked), on top of the SameSite cookie. |
| Guessing | After 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.
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-keyon 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 rotateadds a new key and makes it active;resealre-encrypts every stored secret under it;retire KEY_IDremoves an old key and refuses while anything still uses it.statusshows 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
showcommands 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 asconf tandwr. 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
showcommands 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 drillrestores 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 file | Key | Checked by |
|---|---|---|
Release bundle (.cenovel) for a new install | Release key | The installer, against /etc/cenovel/trust |
| Update bundle | Update key | The root updater, against /etc/cenovel/trust/update.pem only |
RELEASE.json (version, schema range, SBOM digest), INTEGRITY.json, site collector releases | Release key | The installer, the console and each collector |
SHA256SUMS for each release | Release key | You, 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-missingCompare the release key’s fingerprint with the one Cenovel gives you separately from the download:
openssl pkey -pubin -in release.pem -outform DER | sha256sumOn 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.
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-adminships 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