Appearance
Security Trust Model
Telovix separates four independent trust domains: control-plane trust between sensors and the Console, policy trust for runtime behavior, operator access trust for the Console UI and API, and commercial trust for entitlements. Each domain has distinct mechanisms and failure modes. Understanding the boundaries prevents misdiagnosis when something goes wrong and prevents incorrect assumptions about what a compromised component can affect.
The four trust domains
| Domain | What it governs | Mechanism |
|---|---|---|
| Control-plane trust | Sensor identity and communication with Console | Mutual TLS with per-sensor client certificates |
| Policy trust | Whether a policy pack may execute on a sensor | Ed25519 signature verification per policy file |
| Operator access trust | Who can read and write Console state | Session authentication, RBAC, optional SSO and MFA |
| Commercial trust | Whether a deployment is entitled to run | Offline Ed25519-signed license bundles |
These four domains are intentionally independent. A compromised enrollment token does not affect policy trust. A license expiry does not revoke sensor certificates. A rogue operator session cannot bypass policy signature verification on the sensor.
Control-plane trust: sensor to Console
Enrollment
During enrollment, the sensor generates a local RSA key pair using rcgen and creates a Certificate Signing Request with CN=<node_name>, O=Telovix Sensor. It presents an enrollment token alongside this CSR to the Console enrollment endpoint. The Console verifies the token and signs the CSR, issuing a client certificate with a 720-hour (30-day) TTL by default.
The enrollment token is a bootstrap secret only. It is consumed at enrollment time. After enrollment, the durable sensor identity is the client certificate, not the token.
The Telovix Console returns:
control_plane_certificate_pem: the signed client certificatecontrol_plane_ca_certificate_pem: the Console's CA certificatepolicy_signing_public_key_pem: the Ed25519 public key for policy verificationcertificate_expires_at: expiry timestamp in RFC3339 formatcertificate_reference: internal reference ID
The sensor atomically writes all of these to the state directory (/var/lib/telovix-sensor by default). The sensor-state.json file records trust_state="trusted" and renewal_status="ok".
All subsequent communication
Every heartbeat, WebSocket connection, and policy download uses outbound mTLS. The sensor presents its client certificate on every request. The Console validates the certificate against its CA on every request. There is no API key or session token fallback for sensor communication.
Renew certificates
Renewal is triggered by two conditions:
| Trigger | Condition |
|---|---|
| Proactive renewal | Certificate expiry is less than 72 hours away |
| Manual renewal | Operator increments the manual_renewal_generation counter via Console |
The renewal process generates a new CSR and submits it to the Console renewal endpoint. The Console issues a new certificate. The sensor writes the new key first, then the certificate, so that a crash mid-write triggers re-enrollment rather than leaving a corrupt state.
The default certificate TTL is 720 hours (30 days). The renewal-due threshold defaults to 86400 seconds (1 day). Both are configurable in Console Settings.
Read trust states
| State | Meaning |
|---|---|
trusted | Certificate is valid; mTLS is working |
renewal_recommended | Cert is approaching expiry but still within tolerance |
renewal_due | Cert expiry is within the renewal threshold; renewal should happen immediately |
trust_revoked | Console revoked this sensor; mTLS will stop working |
When trust state is trust_revoked, the sensor stops forwarding events and the Console rejects all requests from that sensor. Trust alerts are visible on the Fleet > Trust Alerts page in the Console and on each sensor's Trust tab.
Understand trust state and enforcement
A sensor whose trust health is not healthy is blocked from transitioning to enforcement mode. The enforcement readiness check requires the sensor to have healthy trust before allowing the enforce_ready or enforced state.
Policy trust: runtime behavior
Verify policy delivery
Policy packs are delivered from the Console to the sensor as YAML files. Each policy file may be accompanied by a .sig sidecar file containing an Ed25519 signature, base64-encoded.
The Console holds an Ed25519 signing key at the path configured during setup (default: policy-signing.ed25519). The sensor receives the corresponding public key during enrollment and stores it at policy-signing.pub.
When loading a policy file, the sensor follows this logic:
- If a
.sigsidecar exists: load the public key frompolicy-signing.pub, verify the signature againsttelovix-policy-v1:{policy_name}\n{yaml_content}, and reject the policy if verification fails. - If no
.sigsidecar exists but a signing key is present: log a warning and proceed (backward compatibility with pre-signing deployments). - If no
.sigsidecar and no signing key: skip verification.
The signature format telovix-policy-v1:{name}\n{content} binds the policy name to its content. A policy file cannot be renamed and applied under a different name without invalidating the signature.
This means a policy injected by an attacker who can write to the sensor's state directory but does not have the Console's Ed25519 private key will be rejected at load time.
Operator access trust
Authentication
Console users authenticate with email and password. Sessions are issued as secure, HTTP-only cookies. TOTP-based MFA is available and can be required per user. The Console supports SAML 2.0 and OpenID Connect (OIDC) for SSO. SSO configuration is stored in the Console database and managed from Settings > SSO in the Console UI.
API keys are scoped alternatives to session authentication for programmatic access. API keys are created by authenticated users and can be revoked individually.
Role-based access control
| Role | Level | What it can do |
|---|---|---|
viewer | 1 | Read-only access to sensors, events, and reports |
analyst | 2 | All viewer permissions plus investigation and alert management |
sensor_owner | 3 | All analyst permissions plus sensor management for owned sensors |
operator | 3 | All analyst permissions plus policy management, enforcement changes, and exercise creation |
admin | 4 | Full access including user management, settings, and license import |
Role levels are enforced server-side on every request. The require_operator and require_admin middleware functions gate the relevant API routes. Role elevation requires an admin user.
Commercial trust: license validation
License structure
The Portal issues signed license bundles. Each bundle is a JSON envelope with the structure:
json
{
"schema_version": 1,
"signature_algorithm": "ED25519",
"payload": {
"customer_id": "...",
"workspace_id": "...",
"license_id": "lic-...",
"plan": "growth",
"max_protected_nodes": 250,
"valid_from": "...",
"valid_until": "...",
"grace_until": "...",
"issued_at": "..."
},
"signature": "<base64-encoded-ed25519-signature>"
}The grace_until field extends the license beyond valid_until for a configurable grace period (default: 30 days). During the grace period, the deployment remains operational but the Console displays an expiry warning.
Signing
The Portal's license signing service exposes a single internal endpoint at POST /internal/v1/sign-license. This service is not reachable from the public internet. It loads the Ed25519 signing key from the path configured at deployment time, serializes the payload to canonical JSON, signs it, and returns the envelope. Communication between the Portal and the signing service is authenticated with a shared secret compared using constant-time comparison.
Console validation
The Console validates license bundles offline. It does not contact the Portal or any external service at validation time. The operator imports the signed bundle via the setup wizard or from Settings > License in the Console. The Console verifies the Ed25519 signature against its embedded public key and checks valid_until and grace_until against the current time.
This offline model means the Console continues operating in air-gapped or network-isolated environments as long as the imported license has not expired. The Console does not depend on Stripe connectivity, Portal availability, or any cloud service for runtime entitlement decisions.
Understand why commercial trust is separate
The Stripe payment flow, Portal workspace state, and billing webhook processing all feed the Portal. The Portal issues a signed license. The Console trusts only the signature on that license bundle, not any runtime state from Stripe or the Portal. This means:
- Stripe refunds, disputes, or service outages do not affect the Console's license state
- A compromised Portal cannot issue a license with capabilities beyond what the Ed25519 signing key authorizes
- A stolen or replayed license bundle is only as dangerous as the private key that signed it
Trust boundary violations and what they mean
| Violation | What is affected | What is not affected |
|---|---|---|
| Stolen enrollment token | Can enroll a rogue sensor during the token's valid window | Does not compromise existing sensors; revoke the token immediately |
| Stolen sensor client certificate | Can send forged heartbeats from that sensor identity | Does not affect other sensors; revoke via Console trust management |
| Compromised policy signing key | Can inject arbitrary policy into sensors that trust this key | Does not affect sensors with no signing key (they skip verification with a warning); rotate the key and re-enroll |
| Expired license | Console blocks new sensor enrollments above the node limit; grace period provides 30-day buffer | Does not revoke sensor certificates or stop active monitoring on enrolled sensors |
| Stolen operator session | Attacker has operator-level Console access | Cannot bypass mTLS on existing sensors; cannot forge signed policy without the Ed25519 key |
Monitor trust from the Console
| Console page | What it shows |
|---|---|
| Fleet (header) | Fleet-wide trust health summary with counts by trust state |
| Fleet > Trust Alerts | All active trust alerts across the fleet |
| Sensors > [sensor] > Trust tab | Detailed trust state, certificate expiry, and renewal status for one sensor |
| Sensors > [sensor] > Trust tab > Trigger Renewal | Triggers manual certificate renewal |
| Settings > License | Import a signed license bundle; view current license status |
Operational guidance
Do not share enrollment tokens across hosts. Each token should enroll one sensor. A token reused across multiple hosts means any one of those hosts can abuse the token window to generate additional enrollments. Generate one token per node or cluster from the Deploy Sensor wizard, then immediately verify the sensor appears in Sensors after enrollment.
Keep the policy signing key offline. The Ed25519 policy signing key should not reside on the Console host. Rotate it if the Console host is compromised. After rotation, re-deploy policy packs with new signatures; sensors will accept old signatures until the pack is replaced.
Separate the license signing key from the Portal. The signing service runs as a separate process (backend-license). The key path should point to a key stored on a separate host or HSM. A compromised Portal cannot issue fraudulent licenses unless it can also reach the signing service and knows the shared secret.
Monitor trust alerts. The Fleet > Trust Alerts page in the Console is the earliest signal of certificate expiry or revocation. Configure notifications for trust alert events so sensor certificate expiry does not cause unexpected disconnections.