Security posture
spane is self-hosted and built to add monitoring without adding exposure. Agents reach in over outbound-only mutual TLS, secrets live in a vault, and the platform ships hardened by default.
spane is open source (Apache-2.0) and undergoes automated (Dependabot, CodeQL, bandit, pip-audit, gitleaks) and manual pre-production review.
How agents connect
Enrollment is the only public step, gated by a one-time token with expiry and max-uses. Everything after is mutually authenticated.
Outbound-only agents
The agent opens no inbound ports. All traffic is outbound HTTPS (443) to your spane server, so monitored hosts expose no new attack surface.
Mutual TLS
TLS 1.3 with client-cert auth and CA pinning. nginx verifies each agent certificate against the agent CA and forwards only the verified serial to the API; unverified requests are rejected at the edge.
Per-agent identity
Each agent holds a unique EC P-384 certificate issued by spane’s internal CA (OpenBao PKI), valid one year and revocable from the UI. The private key is generated on the host and never leaves it.
Secrets in a vault
Device credentials, tokens, and feed keys live in OpenBao, not the database — Postgres stores only vault path references. Nothing sensitive is logged or returned by the API.
Least-privilege agent (Linux)
- Runs as a dedicated low-privilege user (netpulse-agent) with no login shell and no home directory.
- Hardened systemd unit: NoNewPrivileges, ProtectSystem=strict, ProtectHome, with ReadWritePaths limited to the config directory.
- Reads only what collectors need — /proc, /sys, /etc/os-release, and service state.
Secure by default
The server side is hardened across authentication, authorization, transport, and input handling.
Password hashing
Argon2id — the memory-hard, OWASP-recommended standard — hashes new and changed passwords; older hashes upgrade transparently on next login.
Role-based access control
Deny-by-default DRF permissions enforce read/write by role, with Admin-only gates on user management and system configuration, plus full audit logging.
Authenticated WebSockets
Live alert / telemetry / topology streams validate a JWT on the WebSocket handshake and reject anonymous connections — no unauthenticated data streams.
Hardened transport
TLS-1.3-only nginx with HSTS, X-Content-Type-Options, X-Frame-Options, and Referrer-Policy; secure, SameSite, HttpOnly cookies in production.
Rate-limited auth
Login and token-refresh endpoints are throttled per real client IP behind the proxy, mitigating credential-stuffing and lockout abuse.
Input hardening
Discovery inputs are validated as strict IP/CIDR (no nmap-flag injection); CSV exports neutralize formula injection; the ORM and DSL queries avoid raw SQL.
Signed agent binaries
We sign released agent binaries so you can verify they came from us and were not tampered with in transit. Verify the signature before promoting a binary into production.
# The spane agent release-signing public key will be
# published here and at the canonical URLs below.
-----BEGIN PUBLIC KEY-----
<published with the first signed release>
-----END PUBLIC KEY-----
# key id : <tbd>
# fingerprint: <tbd> - This page (
spane.app/security) — canonical copy - The GitHub repository, committed under version control
- Attached to each signed GitHub release
How to verify (once published)
# 1. fetch the binary + its detached signature
curl -fLO .../netpulse-agent-linux-amd64
curl -fLO .../netpulse-agent-linux-amd64.sig
# 2. verify against the published public key
openssl dgst -sha256 \
-verify spane-agent.pub \
-signature netpulse-agent-linux-amd64.sig \
netpulse-agent-linux-amd64
# → Verified OK The exact tool and command will be finalized with the signing scheme and pinned here when the key ships. The page and verification flow are in place now so nothing changes for you but the key itself.
Reporting a vulnerability
Found a security issue? Please report it privately through the GitHub repository’s security advisories rather than a public issue, and give us a reasonable window to remediate before disclosure.