Security & architecture
Nexus is self-hosted by design. It runs on your server, reads your helpdesk data there, and never sends that data to us. Here's exactly how it's built and what protects it — because "trust us" isn't a security model.
Your data stays on your infrastructure
The Nexus server runs in your environment (Docker, a VPS, Plesk, wherever) and talks to your helpdesk from there. Customer data — tickets, ratings, agent stats — is processed on your deployment and is never transmitted to us. The desktop, mobile and wall-TV clients are stateless renderers: all state lives on your server's /data volume, so a client can never leak or lose anything.
The one exception is the optional AI features, off by default: if you switch them on, Nexus sends data to the AI provider you configure with your own key — the daily summary sends only aggregate numbers, and Switchboard's AI routing sends a ticket's subject/body (only for tickets its built-in router is unsure about, always with an on-device fallback). That goes to your chosen provider under their terms, never to us; leave AI off and nothing leaves your server.
If you choose managed hosting (our optional add-on — self-hosting is the default), that trade is explicit and different: we run your own isolated instance for you, so your board's data lives on infrastructure we operate in a UK/EU region rather than on your hardware. It's a dedicated instance per customer — never a shared multi-tenant pool — your secrets and helpdesk tokens are encrypted at rest, and you can export your data and move to self-hosting at any time. We tell you this plainly because "self-hosted, your data stays put" is the honest default, and hosting is a deliberate, informed choice to hand that operation to us.
Secrets encrypted at rest
Every secret — your helpdesk API token (Zendesk, Freshdesk, Freshservice, Jira SM, HaloPSA, SuperOps, NinjaOne, ConnectWise, Zoho Desk, Help Scout or Front), the alert webhook URL, your monitoring credentials (UniFi, Meraki, Aruba, Omada, SuperOps, NinjaOne, N-able, Datto RMM, Kaseya VSA, ConnectWise Automate, Auvik, Domotz, Acronis, Huntress and Liongard) and any LLM API key — is encrypted before it touches disk. Non-secret settings live in settings.json; secrets are kept apart in secrets.json on the /data volume, so the two never mix. The backend is chosen automatically per OS:
- macOS / Linux → AES-256-GCM. Each value gets a fresh random 16-byte salt and 12-byte IV. The 256-bit key is derived with scrypt from
DASHBOARD_SECRET_PASSPHRASE, so the same secret encrypted twice produces two unrelated blobs. The stored form isaesgcm:+ base64 ofsalt | iv | tag | ciphertext. - Windows → DPAPI. Windows' Data Protection API encrypts the value bound to the current user + machine — no passphrase to manage, and a copied
secrets.jsonis useless on any other account or box. Stored with adpapi:tag.
Every value is authenticated: on macOS/Linux the GCM auth tag is verified on decrypt, so a tampered or truncated blob is rejected rather than silently mis-read. Because each blob is prefix-tagged (aesgcm: / dpapi:), the server always knows which scheme produced it and can never decode one as the other.
dpapi: tag). The GCM auth tag is checked on every decrypt. (Diagram uses your selected theme's colours.)The web API only ever reports whether a secret is set — a boolean — and never returns the secret value itself, not even masked. And by design there is no recovery path: lose the DASHBOARD_SECRET_PASSPHRASE (or move a DPAPI blob to a different user/machine) and the encrypted secrets are gone for good. Back the passphrase up alongside your /data volume.
No phone-home to render your board
Your licence is a signed token verified offline with a bundled Ed25519 public key — the board renders with zero outbound calls to us. The only contact with our licence service is a periodic, SSRF-guarded heartbeat to confirm the subscription is still active (it fails open for a grace window, so a blip never blanks your wall), plus a minimal telemetry beacon — on by default for licensed installs, one switch to turn off — described below.
Browser-facing hardening
Every response from a Nexus server, the licensing service and this site carries a baseline of security headers: X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, frame protection (SAMEORIGIN on your server, DENY + frame-ancestors 'none' on the licensing portal) and HSTS wherever the request genuinely arrived over HTTPS — never pinned onto a plain-HTTP LAN install, which would break it. The licensing portal's HTML additionally ships a Content-Security-Policy restricting sources to the portal's own origin, and authenticated portal pages are Cache-Control: no-store so they can never land in a shared cache.
The dashboard's own view passcode is stored only as a salted scrypt hash (the passcode itself is never written anywhere); device sessions are an HMAC over that hash, so changing the passcode instantly signs out every device. Logins are throttled per-IP and by a global ceiling, so rotating addresses can't buy unlimited guesses — and on a fresh install the first passcode requires a one-time setup code printed only to the server console, closing the first-visitor takeover window. On POSIX systems the secrets and auth files are written owner-only (0600). The licensing portal itself is password + email 2FA, with server-side sessions you can revoke and rate limits that hold even under concurrent attempts.
Outbound requests are SSRF-guarded
Everywhere Nexus fetches a URL you configure — an on-call calendar, an alert webhook, an AI-summary provider — the request is checked first: only public http(s) hosts are allowed, private / loopback / link-local / cloud-metadata addresses are refused, and redirects are blocked so a response can't bounce the request to an internal target. A crafted URL can't be used to probe your internal network.
Plan limits enforced on the server
Paid features aren't just hidden in the UI — the server refuses the underlying API calls for plans that don't include them, and a licence that's lapsed locks the board server-side. The client overlays are the soft-sell; the server is the gate.
Access control you choose
Read access is open by default, because a wall TV shouldn't need a login on a trusted LAN. When you expose a board more widely, you have layers:
- a view passcode, and a separate admin token for configuration changes;
- access links (Enterprise) — shareable view-only URLs, optionally scoped to a single client organisation with the data isolation enforced on the server, not the client;
- an IP/CIDR allowlist (
READ_ALLOWLIST) as a hard network gate; - and the usual HTTPS at a reverse proxy + VPN/SSO in front of it all.
Browser sessions use httpOnly cookies, and access-link keys are exchanged once via POST so the secret never lingers in a URL or proxy log.
Telemetry — minimal & opt-out when you self-host
Self-hosted (the default). If you leave it on, Nexus sends a small beacon every ~30 minutes — an opaque licence id, the running version, the plan name, the helpdesk product it's connected to (the product name only — e.g. zendesk) and an optional instance label. That's it: no customer data, no helpdesk content, nothing that identifies a person. It exists only for version-adoption and provider-mix figures, and you can turn it off entirely in Settings → Licence or with NEXUS_TELEMETRY_OPTOUT=1.
Managed hosting — the same minimal envelope, always on. If you take our optional hosting, the board checks in about every five minutes and this can't be turned off. It still sends exactly { licenceId, version, plan, provider, instanceName? }. There is no hosted flag, uptime, health status, provider reachability, error count or error string — and never ticket content, credentials or end-customer data. The licence service adds a last-seen timestamp when it stores the beacon, which lets us detect a silent board. Self-host instead and the same minimal envelope is opt-out, as above.
Hardened by default, audited continuously
- The Docker container runs as an unprivileged user, not root.
- The licensing service verifies every Stripe webhook signature before trusting it, and signs licences with a key that never leaves the vendor.
- Dependencies are watched by Dependabot, a CycloneDX SBOM is generated for each build, and the project carries a security test suite that runs on every change.
Reporting a vulnerability
Found something? Please email [email protected] with the details rather than opening a public issue, and give us a reasonable chance to fix it before disclosing it elsewhere. We acknowledge reports within three working days and are glad to credit responsible disclosure.
In scope: the Nexus application (desktop, web/server, mobile and the Zendesk app), the licensing service and this website. Out of scope: your own self-hosted deployment's configuration, third-party helpdesk providers, and findings that require a compromised host or physical access.
Helpful to include: the affected version or URL, clear steps to reproduce, the impact you think it has, and any proof-of-concept — but please don't run tests that could affect other users' data or availability. The full self-hosting hardening checklist lives in the docs.
Accessibility
Nexus is built for big rooms: high-contrast dark themes, large readable type, and a board that scales to fit any screen. The admin interface is keyboard-navigable and uses semantic markup. Accessibility is an ongoing effort — if something doesn't work for you or your team, please tell us and we'll fix it.