Lightweight macOS detection sidecar for Santa that evaluates Endpoint Security telemetry locally with CEL rules and forwards only matched detection signals to a backend server.
Experimental. Built for home labs and small fleets. Early release – expect bugs and API changes.
Santamon reads Santa's protobuf telemetry stream, evaluates detection rules using CEL expressions, and ships security signals to a backend. Raw telemetry stays on the endpoint—only detections are forwarded.
Core capabilities:
- Local detection: CEL-based rules evaluate events on-device
- Three rule types: Simple matching, time-window correlation, baseline (first-seen)
- Process lineage: Optionally attach full process trees to execution signals
- Embedded state: BoltDB tracks correlations, first-seen data, and signal queue
- Resilient shipping: Concurrent batching, retry logic, circuit breaker
Santamon is a detection sidecar for Santa, not another ESF client.
Building a custom ESF tool requires Apple's restricted entitlements, provisioning profiles, and careful handling of high-volume Endpoint Security events. Santa already does this and is battle-tested in production.
Santamon's value:
- Reuses Santa as the ESF sensor layer (no additional entitlements)
- Runs rules locally close to the data, instead of streaming everything
- Lightweight state via BoltDB for correlations and deduplication
- Low infrastructure cost - only ships high-signal detections
Santa handles the heavy lifting of ingesting Endpoint Security events reliably and safely; Santamon focuses on detection logic and signal quality.
Santa Spool → Watcher → Decoder → Rules Engine → Signal Generator → Shipper → Backend
↓ ↓
┌────────────────────────┐
│ State DB (BoltDB) │
│ • Correlation windows │
│ • Baseline tracking │
│ • Signal queue │
└────────────────────────┘
Process lineage: in-memory cache (1h TTL, 50K max)
Data flow:
- Watcher monitors Santa's spool directory (
/var/db/santa/spool/new/) for new protobuf files - Decoder reads and decompresses protobuf messages from spool files
- Rules Engine evaluates events against CEL expressions (simple, correlation, baseline rules)
- Signal Generator creates context-rich signals for rule matches (with optional process trees)
- Shipper batches and sends signals to backend via HTTPS with retry logic and circuit breaker
- State DB persists correlation state, baseline tracking, signal queue, and spool journal
Spool lifecycle:
- Spool files with no detections are deleted after processing to keep Santa's spool from filling
- Files that produced detections are archived to
santa.archive_dir(default:/var/lib/santamon/spool_hits) - Signals include the archived spool path when available so you can retrieve the protobuf if needed
Process Lineage:
- In-memory cache of recent process execution history
- Enables full process tree context for execution detections
- TTL: 1 hour | Max: 50K entries (LRU eviction)
- Boot session isolated (no cross-boot ancestry)
- See RULES.md for usage
- macOS 15.4+ (some telemetry types like
tcc_modificationrequire macOS 15+) - Santa with protobuf telemetry from northpolesec/santa
- See Santa telemetry docs
- Example config:
configs/examples/santa-config.mobileconfig
- Go 1.23+ (for building from source)
Santa must be configured to write protobuf events. Use the provided configuration profile:
# Review and customize, then install via System Settings
open configs/examples/santa-config.mobileconfig
# Verify
santactl status | grep "Log Type"
# Should show: Log Type | protobufgit clone https://github.com/0x4d31/santamon.git
cd santamon
make buildsudo make installThis installs:
- Binary:
/usr/local/bin/santamon - Config:
/etc/santamon/config.yamlandrules.yaml - LaunchDaemon:
/Library/LaunchDaemons/com.santamon.plist - State directory:
/var/lib/santamon/
Edit /etc/santamon/config.yaml:
shipper:
endpoint: "https://your-backend.example.com:8443/ingest"
api_key: "${SANTAMON_API_KEY}"Set the API key in the LaunchDaemon plist:
# Generate strong API key
openssl rand -hex 32
# Edit LaunchDaemon
sudo nano /Library/LaunchDaemons/com.santamon.plist
# Add under EnvironmentVariables:
<key>SANTAMON_API_KEY</key>
<string>your-generated-key-here</string># Start service
sudo make start
# Monitor logs
make logsMain config: /etc/santamon/config.yaml
Minimal configuration example
agent:
id: "${HOSTNAME}"
shipper:
endpoint: "https://backend.example.com:8443/ingest"
api_key: "${SANTAMON_API_KEY}"Key settings
santa:
spool_dir: "/var/db/santa/spool" # Santa spool location
archive_dir: "/var/lib/santamon/spool_hits" # Archive spool files that produced alerts
stability_wait: "2s" # Wait before reading new files
rules:
path: "/etc/santamon/rules.yaml" # File or directory
state:
db_path: "/var/lib/santamon/state.db"
sync_writes: true # Fsync after writes (safer but slower)
first_seen:
max_entries: 10000 # LRU cache for baseline rules
windows:
max_events: 1000 # Max events per correlation window
shipper:
batch_size: 100 # Signals per batch
flush_interval: "30s" # Time between flushes
timeout: "10s" # HTTP request timeout
tls_skip_verify: false # NEVER true in productionSee configs/santamon.yaml for all options with detailed comments.
Rules are CEL expressions that evaluate Santa events. Three types supported: simple, correlation, and baseline.
Simple rule example
rules:
- id: SM-014
title: "Non-interactive process invoking curl/wget"
description: |
Non-terminal, non-package-manager process launching curl or wget.
expr: |
kind == "execution" &&
event.execution.target.executable.path in ["/usr/bin/curl", "/usr/bin/wget"] &&
// Exclude interactive shells
!(
event.execution.instigator.executable.path.startsWith("/bin/bash") ||
event.execution.instigator.executable.path.startsWith("/bin/zsh") ||
event.execution.instigator.executable.path.startsWith("/bin/sh")
) &&
// Exclude Homebrew / package-manager helpers that legitimately use curl frequently
!(
event.execution.instigator.executable.path.startsWith("/opt/homebrew/") ||
event.execution.instigator.executable.path.contains("/Homebrew/")
)
severity: high
tags: ["T1105", "command-and-control"]
extra_context: ["event.execution.args"]
include_process_tree: true
enabled: trueCorrelation rule (multiple events in time window)
correlations:
- id: SM-COR-001
title: "Process touching multiple credential stores"
description: "Single process accessing 3+ credential stores within 5 minutes."
expr: |
kind == "file_access" &&
event.file_access.policy_name in [
"ChromeCookies", "CometCookies", "SSHPrivateKeys",
"BrowserPasswords", "KeychainDB"
]
window: "5m"
group_by: ["event.file_access.instigator.executable.path"]
count_distinct: "event.file_access.policy_name"
threshold: 3
severity: critical
tags: ["T1539", "T1552", "credential-access"]
enabled: trueBaseline rule (first-seen detection)
baselines:
- id: SM-BASE-001
title: "First-time unsigned binary executed from user paths"
description: "First time an unsigned binary executes from /Users paths."
expr: |
kind == "execution" &&
event.execution.decision == DECISION_ALLOW &&
event.execution.target.executable.path.startsWith("/Users/") &&
(
!has(event.execution.target.code_signature) ||
!has(event.execution.target.code_signature.team_id) ||
event.execution.target.code_signature.team_id == ""
)
track: ["event.execution.target.executable.cdhash"]
learning_period: "720h"
severity: high
tags: ["T1204.002", "initial-access"]
enabled: trueRule organization: Single file (/etc/santamon/rules.yaml) or multi-file directory structure.
Validate before deploying:
santamon rules validateSee RULES.md for comprehensive guide.
Santamon requires a backend to receive signals. A minimal FastAPI backend is included in backend/.
What it does:
- Receives signals via
POST /ingest(requires API key) - Stores signals in SQLite database
- Provides query API (
GET /signals,GET /stats) - Tracks agent health via heartbeats (
POST /agents/heartbeat) - Web UI for signal management
Quick start:
cd backend
pip install fastapi uvicorn
# Set API key
export SANTAMON_API_KEY="your-key-here"
# Run (uses HTTPS if cert.pem exists, otherwise HTTP)
python backend.pySee backend/README.md.
# Run agent (foreground, verbose mode)
santamon run --verbose
# Validate rules
santamon rules validate
# Show status
santamon status
# Database operations
santamon db stats # Show statistics
santamon db compact # Compact database
# Version
santamon version- RULES.md - Detection rule writing guide
- SECURITY.md - Security considerations and agent resilience
- backend/README.md - Backend deployment guide
- configs/santamon.yaml - Full configuration reference
- configs/rules.yaml - Example detection rules