Dagda

Security & data handling

What agents read

An agent reads only what it needs to compute the metrics and alerts you've enabled: /proc (CPU, memory, load, disk I/O, network), nginx/PHP-FPM/MySQL logs and config it can find on the host, UFW and fail2ban status, and package activity logs — the same sources the original single-server monitor this product is built from has always read. It never reads application data, database contents, or file contents outside the specific config/log paths each module documents.

What is sent

An agent sends metric snapshots, minute-rollup history, and events to your hub only — over outbound HTTPS, Authorization: Bearer <per-host key>, gzip-compressed, capped at 5 MB per push on the wire. No monitoring data is ever sent to Old Cart Technology LLC. The only outbound traffic to Old Cart infrastructure is:

  • APT repository access for package updates (apt.dagdamonitor.com, using your license's repository token), from the hub and from agents;
  • during install, a one-time script fetch (get.dagdamonitor.com);
  • the hub's automatic license check (on by default, can be turned off): about once a day it asks https://dagdamonitor.com/api/license whether a newer license exists for its license ID. It sends the license ID, the issue date of the installed license and a one-time proof that it holds the repository token (an HMAC over a fresh server challenge) — the token itself is never sent. See Licensing below.

Where data lives

Everything — the database, configuration, TOTP secrets, license — lives on your own hub, in /var/lib/dagda and /etc/dagda. There is no multi-tenant hosted service; Dagda never phones home with your monitoring data.

Accounts & sessions

Sign-in is TOTP-based (6-digit codes, ±1 step, replay-protected per user) plus, if you've lost your device, one of 10 single-use recovery codes; an optional email one-time code is also available if [email] is configured. A signed-in session is tracked by a cookie named __Host-dagda, set Secure; HttpOnly; SameSite=Strict, valid 24 hours; only its SHA-256 hash is stored server-side, so a stolen database can't be replayed as a live session. Setup, invite and password-reset links are single-use, expire, are shown to you exactly once, and — like agent keys and session tokens — are stored only as a SHA-256 hash. Sign-in attempts are rate-limited per IP, per account and globally, with a lockout after repeated failures.

Encryption at rest

TOTP secrets are sealed with AES-256-GCM using a per-install master key at /etc/dagda/master.key (0600, root-only, generated at install time). Agent keys, invite/join/session tokens are random 256-bit values stored only as SHA-256 hashes, never in plaintext. Recovery codes are hashed with HMAC-SHA-256 keyed by that same master key (not a plain hash) — a leaked recovery_codes table can't be brute-forced offline without also stealing the master key.

Everything else in the database (metric history, alerts, the audit log, server details, user email addresses) is stored in plain SQLite, protected by file permissions (the data directory is 0700, root-only), not by encryption. Treat the data directory and every backup as sensitive.

Browser security

The hub sends a Content-Security-Policy that allows no inline script or style anywhere in the UI (script-src 'self', style-src 'self', no unsafe-inline/unsafe-eval), plus X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy: no-referrer, Cross-Origin-Opener-Policy: same-origin, a Permissions-Policy that blocks camera/microphone/geolocation, and X-Robots-Tag: noindex, nofollow. API responses are sent with Cache-Control: no-store.

Network requirements

  • Agents need outbound HTTPS (443) to your hub for pushes, and outbound HTTPS to the Old Cart APT repository (apt.dagdamonitor.com) for install and updates. That is not the whole list: every install, hub or agent, also monitors its own locally discovered sites and domains, so an agent additionally needs outbound HTTPS to rdap.org for domain-expiry checks (falling back to whois, TCP 43, when RDAP has no answer) and to the sites it monitors, for TLS-certificate checks. Still, no inbound ports at all — an agent runs no web server and has no users.
  • The hub needs inbound HTTPS (443) from browsers and enrolling agents, outbound HTTPS to your email provider if configured, outbound HTTPS to the Old Cart APT repository (apt.dagdamonitor.com) for updates, and outbound HTTPS to dagdamonitor.com for the automatic license check (unless you turn it off). Like any install, it also checks its own sites and domains, so it needs the same outbound access described above for agents (rdap.org/whois and the sites it monitors). Optional features add their own outbound HTTPS destinations: any alert channel you configure (ntfy, Pushover, Telegram, a heartbeat/health-check URL). It only trusts the X-Real-IP header on connections arriving from the loopback address (127.0.0.1 or ::1), so if you put another reverse proxy or CDN in front of Dagda's own nginx, that proxy must set X-Real-IP itself or sign-in and enrollment rate limiting will key off the wrong address.

Licensing

License files are verified entirely offline. Each license carries a certificate: Old Cart's root key — kept off every server, on encrypted offline media — certifies an issuing key for at most a year, and that issuing key signs your license. Dagda embeds only the root public key and checks the whole chain, plus a list of revoked certificates that ships with each Dagda release, without contacting anyone. A license stays valid until its own expiry date even after the certificate that signed it has expired.

Automatic license updates. So that renewals, tier changes and a trial-to-paid switch arrive without you copying files, the hub checks once a day (hourly while on a trial or close to expiry) for a newer license for its license ID, and installs one only if it verifies, belongs to the same license ID, is newer than the installed license and has not expired. Turn it off under Admin → License ("Install renewed licenses automatically"); Check now and installing a license file by hand still work. The check never affects monitoring: if the license server cannot be reached, nothing changes. The once-a-day "update available" check (below) reads only your own package manager.

Version 1 license files (issued before Dagda 0.2.0) are no longer accepted; Old Cart re-issues them as version 2 under the same license ID and repository token.

Update checks

At most once a day, the hub runs apt-cache policy dagda — a read-only, entirely local command — and compares the installed and candidate package versions. It relies on the host's own periodic apt update (cron or unattended-upgrades); the service itself never runs apt update and never contacts Old Cart's servers as part of this check.

Backups

dagda backup bundles the database, dagda.toml, master.key and the installed license into a single .tar.zst/.tar.gz archive, written mode 0600 inside a 0700 directory. Because a backup contains master.key, it must be protected exactly like the key itself — anyone who has the archive can decrypt every TOTP secret it seals. Copy backups off the host (a backup stored only on the machine it protects doesn't survive losing that machine), and give off-host copies the same access controls you'd give the live key. The archive itself is not encrypted, so encrypt off-host copies yourself (e.g. with age or gpg) rather than relying on file permissions alone once a copy leaves this host.

Support bundles

dagda support-bundle [--mask-ips] [--out <file>] produces a redacted diagnostic archive meant to be attached to a support request. Included: the active configuration with every secret value replaced, dagda doctor's full check output, a license summary (licensee and expiry only), recent service-journal lines, and version/platform info — all passed through the same redactor, which strips tokens, keys and passwords (and, with --mask-ips, IP addresses) before anything touches disk. Never included: the database, the master key, the raw license file (its signature and repository token), or your server list. Redaction is defense in depth, not a substitute for looking at what you're about to send — open the archive and skim it before you attach it anywhere. Like a backup, the bundle itself isn't encrypted, so encrypt it too (e.g. age or gpg) before sending it anywhere you don't fully trust.

Hardening

dagda.service runs with systemd sandboxing: NoNewPrivileges=yes, ProtectSystem=strict with write access narrowed to /var/lib/dagda (and /etc/apt/auth.conf.d, for license-repo credentials), ProtectHome=read-only, PrivateTmp=yes, kernel/clock/cgroup protections, RestrictSUIDSGID, LockPersonality, RestrictRealtime, RestrictNamespaces and a native SystemCallArchitectures. It currently runs as root, because it reads root-only logs (journald, UFW, fail2ban) that a monitoring product on this stack needs. A dedicated, less-privileged service user is planned future work, not yet implemented. File modes are enforced and checked by dagda doctor: /etc/dagda and the data directory are 0700; dagda.toml and master.key are 0600. Secrets are never written to logs, never sent to the browser, and never included in a support bundle unredacted.

If you lose master.key

Every sealed TOTP secret and every recovery-code hash depends on master.key. Lose it without a backup and they become permanently unrecoverable — there's no way to unseal or re-verify them. Affected users would need an admin to reset their authenticator (dagda user reset <email>) and re-enroll. This is exactly why dagda backup includes master.key, and why backups need the same protection as the key itself.

Reporting a vulnerability

Found a security issue in Dagda? Email support@dagdamonitor.com with details; see https://dagdamonitor.com for more about the product.