GitHub
11/09/2026, 1:05 PM--santa-enabled / SERVICE_SANTA_ENABLED / service.santaEnabled. Enable it on both osctrl-tls and osctrl-api. When the flag is off:
• no tables are migrated
• no routes are registered
• /api/v1/features reports santa: false
osctrl-tls: sync protocol endpoint
• POST /{env}/santa/sync/{stage}/{machineid} implements the Santa sync v2 protocol: preflight, eventupload, ruledownload (paginated, 500 rules per page) and postflight.
• Santa's SyncBaseURL is https://<tls host>/<env uuid>/santa/sync/.
• Requests can be JSON or protobuf, optionally gzip/deflate compressed. Responses use the same encoding as the request.
• Rule changes reach clients by digest:
• At preflight, osctrl computes a SHA-256 of the machine's effective rules.
• If the digest differs from the last applied one, osctrl answers `CLEAN_RULES`; otherwise NORMAL.
• If rules change mid-sync, ruledownload returns 409 and the client resyncs next time. A client never ends up with a mixed rule set.
• Machine config (MONITOR/LOCKDOWN, batch size, transitive rules, USB blocking) is pushed at preflight.
osctrl-api: management API (/api/v1/santa/{env}/...)
• GET/PUT config
• `POST token`: generate or rotate the token
• GET/POST rules, DELETE rules/{id}
• GET machines, GET events
• All routes require environment admin and write audit log lines (santa: <action>).
• Rules can be scoped to the environment, a tag, or a node. The most specific scope wins: node > tag > env.
• Rule types: BINARY, CERTIFICATE, TEAMID, SIGNINGID, CDHASH. Policies: ALLOWLIST, ALLOWLIST_COMPILER, BLOCKLIST, SILENT_BLOCKLIST, CEL.
• Rules are validated and normalized server-side:
• hashes are lowercased, TEAMID and SIGNINGID team prefixes uppercased
• fields that don't apply are cleared
• length limits are enforced
• Duplicate rules return 409.
• Events are pruned daily, with a fixed 30-day retention.
• osctrl-api.yaml is regenerated.
Frontend: new Santa page per environment
Visible to environment admins when the feature is enabled.
• Rules: list, add and delete rules.
• Events: paged execution events. The Allow / Block buttons prefill a BINARY rule for the file's hash.
• Machines: synced clients with mode, rule count, Santa version and last sync time.
• Settings:
• client config
• token rotation (asks for confirmation when replacing an existing token; the token is shown once)
• the SyncBaseURL and SyncExtraHeaders values for the MDM profile
• Strings added to all 21 locales.
Posture
• New darwin check `santa_agent`: passes when the Santa system extension is activated_enabled. Seeded disabled.
Persistence
• New tables, created by AutoMigrate only when the feature is enabled: santa_env_configs, santa_rules (unique index on env, type, identifier and scope), santa_machines, santa_events.
• Existing tables are unchanged.
Dependencies
• buf.build/gen/go/northpolesec/protos/protocolbuffers/go (Santa sync v2 protos)
• <http://google.golang.org/protobuf|google.golang.org/protobuf> becomes a direct dependency
Security and operational impact
• Authentication
• Each environment has one token, sent by Santa in the X-Osctrl-Santa-Token header.
• Only its SHA-256 is stored, and it is compared in constant time.
• The plaintext is returned once with Cache-Control: no-store and is never readable afterwards.
• Node ownership
• The machine ID must be the UUID of a node already enrolled in that same environment.
• A malformed ID gets 400.
• An unknown environment, unknown machine, machine from another environment, or wrong token all get 403, so responses don't reveal which environments or machines exist.
• Limits
• The raw body is capped at 10 MiB and the decompressed body at 20 MiB (413 past either).
• Unknown protobuf/JSON fields are discarded.
• Audit flooding
• Failed sync authentications are written to the audit log, capped at 60 per hour per environment.
• Dropped writes are counted in santa_auth_audit_dropped_total.
• No IP rate limit, so fleets behind one NAT aren't throttled.
• Trust model limit (documented)
• The token is shared by every Santa host in an environment, and hardware UUIDs aren't secret.
• So any host holding the token can sync as another enrolled node in that environment: it can read that node's scoped rules and upload events as it.
• Recommended mitigation: client mTLS at the reverse proxy, using Santa's ClientAuthCertificate* keys.
• Untouched routes: the existing osctrld script route and all osquery TLS routes are unchanged.
• Docs: ARCHITECTURE.md updated. pkg/santa/README.md documents the protocol, the auth model and the MDM profile. deploy/config/{tls,api}.yml gain santaEnabled: false.
Not included (follow-ups)
• Sync stages commands, metrics, `signalupload`; FCM/NATS push
• File-access, network-flow and telemetry rules; bundle uploads
• Forwarding Santa events to alert rules or log sinks; MCP tools
• Editing rules in place; event search; per-machine mode overrides; bulk rule import
• Sending removable_media_policy instead of the deprecated block_usb_mount
• Configurable event retention
Validation
• `go test ./...`: pass (59 packages)
• `make frontend-test`: pass (785 tests, tsc clean)
• `make openapi-check`: clean
• `golangci-lint`: no new issues (13 existing errcheck findings in pkg/logging and carves.go, which this PR doesn't touch)
Not yet validated:
• End-to-end against a real Santa client on macOS
• Postgres and MySQL; only SQLite was exercised in tests
• Native-speaker review of the non-English translations
jmpsec/osctrlGitHub
11/09/2026, 8:29 PM