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/licensewhether 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 tordap.orgfor 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 todagdamonitor.comfor 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 theX-Real-IPheader on connections arriving from the loopback address (127.0.0.1or::1), so if you put another reverse proxy or CDN in front of Dagda's own nginx, that proxy must setX-Real-IPitself 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.