Skip to content

Security

TheMinecraftGuyGuru edited this page Jun 8, 2026 · 4 revisions

Security Model

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.


How Security Works (Plain Language)

Think of MuxCore security like a building with multiple layers:

  1. Front door (Authentication) — Who are you? Prove it with a password, API token, or SSO.
  2. Room keys (Authorization) — What are you allowed to do? Admins can do anything. Viewers can only browse.
  3. Security cameras (Audit Logging) — Everything is recorded: who did what, when, from where.
  4. Locks on internal doors (Call Policy) — Even modules need permission to talk to each other.
  5. Encrypted hallways (TLS) — Data in transit is always encrypted between machines.

Authentication

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.

API Tokens

{
  "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.


Authorization (RBAC)

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

Policy Example

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: ["*"]

Inter-Module Security

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.


Audit Logging

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

Default Audit Logger

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.


Network Security

API Server (HTTP)

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

Trusted Proxies

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.

WAL and Audit Integrity

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.

Health Endpoint Hardening

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).

Audit Log fsync

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.

gRPC SetHeader Error Handling

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 Mesh (Inter-Machine)

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

Cluster Join Token

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


Data Protection

Encryption at Rest

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 data

Secrets Management

API 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.

Data Redaction

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"}

Module Sandboxing (Planned)

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

Security Roadmap

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

Next Steps

Clone this wiki locally