-
Notifications
You must be signed in to change notification settings - Fork 0
Security
Security is woven into every layer of MuxCore. Most self-hosted media software treats security as an afterthought — local-only access, no audit trail, hardcoded API keys. MuxCore builds authentication, authorization, encryption, and audit logging into the platform from day one.
Think of MuxCore security like a building with multiple layers:
- Front door (Authentication) — Who are you? Prove it with a password, API token, or SSO.
- Room keys (Authorization) — What are you allowed to do? Admins can do anything. Viewers can only browse.
- Security cameras (Audit Logging) — Everything is recorded: who did what, when, from where.
- Locks on internal doors (Call Policy) — Even modules need permission to talk to each other.
- Encrypted hallways (TLS) — Data in transit is always encrypted between machines.
Authentication is module-driven — the core doesn't authenticate users directly. You install an auth module that connects to your existing identity system:
| Auth Module | How It Works |
|---|---|
| Local Accounts | Username + password + optional 2FA, stored locally |
| API Tokens | Scoped, revocable tokens for programmatic access |
| OAuth/OIDC | Authentik, Authelia, Keycloak, Google, GitHub |
| LDAP / Active Directory | Enterprise directory integration |
| Plex Auth | Use your existing Plex account |
Multiple auth modules can be active simultaneously. Your family uses Plex auth. Your scripts use API tokens. Your admin uses OIDC.
{
"name": "readonly-monitor",
"scopes": ["events:read", "status:read"],
"expires": "2027-01-01T00:00:00Z"
}Tokens are scoped and revocable. A monitoring dashboard gets read-only access. A download automation script gets download permissions only.
Once authenticated, every action is checked against role-based permissions:
| Role | What They Can Do |
|---|---|
| Admin | Full system access — manage users, modules, settings |
| Manager | Manage media — approve requests, edit metadata, delete |
| User | Request media, view library, manage their own requests |
| Viewer | View library only — no requests, no changes |
| Module | Scoped to the module's declared needs |
policies:
- role: user
can: [media.request, media.view]
on: [movies, tv, music]
- role: manager
can: [media.approve, media.delete, media.edit]
on: [movies, tv, music, books]
- role: admin
can: ["*"]
on: ["*"]Modules need permission to talk to each other. A downloader shouldn't be able to call the user management module. The Call Policy system enforces this:
// Module declares who it is
ctx = contracts.WithCallerID(ctx, "downloader-qbittorrent")
// Mesh client checks: "can downloader-qbittorrent call GetUsers on auth-local?"
result, err := meshv1.NewModuleMeshClient(conn).Call(ctx, &meshv1.CallRequest{TargetModule: "auth-local", "GetUsers", payload)
// → Denied. Downloaders can't access user management.When no call policy module is registered, inter-module calls are denied by default (secure-by-default). Deployments that want open mode must register an explicit permissive call policy provider.
Every significant action is recorded:
[2026-05-26 14:32:01] user:ender action:media.request resource:"The Terminator" trace:abc123
[2026-05-26 14:32:05] system action:workflow.started resource:movie-request trace:abc123
[2026-05-26 14:32:45] module:downloader-qbittorrent action:download.started resource:torrent:xyz trace:abc123
[2026-05-26 15:45:12] module:media-movies action:library.item.added resource:"The Terminator" trace:abc123
Audit entries include:
- Who — user ID, system, or module ID
- What — the action performed
- On what — the resource affected
- When — timestamp
- Trace ID — links related actions across services
- PrevEntryHash — SHA-256 of the previous entry, forming a tamper-evident chain
- Signature — optional HMAC-SHA256 for cryptographic verification
Core ships with a built-in JSONL file logger. Set audit.path in muxcore.json or MUXCORE_AUDIT_PATH environment variable to enable it:
{
"audit": {
"path": "/var/log/muxcore/audit.jsonl"
}
}When no path is configured, the audit logger is a no-op — all Log/Query/Export calls return immediately with zero overhead. This means audit logging is safe to wire by default and costs nothing until you turn it on.
For production, swap in a module-based audit logger (e.g., database-backed with search indexing) by implementing the AuditLogger contract.
TLS is configured with certificate and key files. Without TLS, the server runs in plaintext (development only):
{
"server": {
"cert_file": "/etc/ssl/cert.pem",
"key_file": "/etc/ssl/key.pem"
}
}Environment variables: MUXCORE_TLS_CERT, MUXCORE_TLS_KEY
The X-Forwarded-For and X-Real-IP headers are only trusted from configured proxy CIDR ranges. By default only loopback (127.0.0.0/8, ::1/128) is trusted. This prevents clients from spoofing their IP for rate-limit bypass:
Client → Internet → Reverse Proxy (10.0.0.1) → MuxCore
↑ trusts XFF only from 10.0.0.0/8
Client cannot set X-Forwarded-For when connecting directly — it's ignored unless RemoteAddr is in a trusted range. Call srv.SetTrustedProxies([]string{"10.0.0.0/8", "172.16.0.0/12"}) to trust additional proxies.
The WAL and audit log now fsync every write to ensure durability on crash. The WAL also syncs on segment rotation. This prevents data loss in the event of a kernel panic or power loss.
The /health endpoint no longer leaks module error messages verbatim. All errors are reported as the fixed string "error" to prevent information disclosure (file paths, internal hostnames, DB credentials in error messages).
Every audit entry is fsynced to disk immediately after writing. Combined with the SHA-256 hash chain and optional HMAC-SHA256 signing, this provides crash-safe, tamper-evident audit logging.
All gRPC metadata propagation errors (term headers on Join, Heartbeat, Members, Watch responses) are now logged at warn level. Previously they were silently discarded, which could cause stale cluster state in clients.
gRPC communication between nodes uses TLS:
{
"grpc": {
"cert_file": "/etc/ssl/grpc-cert.pem",
"key_file": "/etc/ssl/grpc-key.pem",
"mtls_enabled": true,
"ca_cert_file": "/etc/ssl/ca.pem"
}
}Plaintext mode logs a warning — it works but is not recommended for production. Mutual TLS (mTLS) verifies both ends of the connection, preventing unauthorized nodes from joining the mesh.
Environment variables: MUXCORE_GRPC_TLS_CERT, MUXCORE_GRPC_TLS_KEY, MUXCORE_GRPC_MTLS_ENABLED, MUXCORE_GRPC_MTLS_CA
Nodes must present a pre-shared token to join the cluster:
{
"grpc": {
"join_token": "my-secret-cluster-token"
}
}Or: MUXCORE_CLUSTER_JOIN_TOKEN=my-sec...ken
Storage overlay modules handle encryption transparently. Data is encrypted before writing to any storage backend:
Module writes data → Encryption overlay encrypts → Storage backend stores ciphertext
Module reads data ← Encryption overlay decrypts ← Storage backend returns ciphertext
The EncryptionProvider contract supports envelope encryption with automatic key rotation:
ep.Encrypt(ctx, plaintext) // produces self-describing ciphertext
ep.Decrypt(ctx, ciphertext) // reads key metadata, selects correct key
ep.RotateKey(ctx) // new key for future encryptions, old key still works for existing dataAPI keys and passwords are never in environment variables or config files. A secrets module handles them:
secrets.Get(ctx, "jackett_api_key") // not os.Getenv("JACKETT_API_KEY")Supported backends: environment variables (development), Vault (production), encrypted files.
Sensitive data (emails, tokens, IP addresses) is automatically stripped from logs and audit trails:
safe := redaction.Redact(ctx, data, []string{"email", "token", "ip_address"})
// {"user_id": "abc123", "email": "***REDACTED***", "action": "login"}External modules will run as separate processes with limited OS permissions:
- Docker/container isolation — recommended deployment pattern
- gVisor/Firecracker — optional microVM isolation for untrusted modules
- Declared permissions — modules declare what they need; core enforces
| Phase | Features |
|---|---|
| MVP (Current) | Local accounts auth, API tokens, RBAC, TLS encryption, audit logging, call policy |
| Phase 2 | OIDC/SSO, LDAP, mTLS enforcement, module permission declarations |
| Phase 3 | Sandbox policies, network segmentation, SIEM integration |
- Deployment — secure deployment patterns
- Contracts Reference — AuthProvider, Authorizer, Encryption interfaces