GitHub
10/27/2026, 10:37 AM-c silently discards every flag and environment variable, and the README showed an example that relied on --interval anyway.
• A config file without an osctrld: section crashed with a nil-pointer panic.
• Misspelled keys (for example insecrue: true) were silently ignored.
• A huge interval overflowed time.Duration and made the daemon sync in a tight loop.
• baseurl accepted a query or fragment (which breaks every request) and a trailing / (which the server answers with a redirect that turns the POST into a GET). The debug log printed it unredacted.
• The packaged sample config set /path/to/... paths that override the OS defaults, and lacked newer options.
• The README recommended --secret on the command line (visible in ps and shell history).
• uninstall left osctrld's own state files behind, including an upgrade snapshot that can contain the enrollment secret.
Change
• Precedence stays, visibly. With a config file, flags and environment variables are still ignored, but one warning now lists the ignored names (never values). README precedence text rewritten, and the --interval example removed.
• Missing osctrld: section returns a clear error (exit 2) instead of panicking.
• Unknown keys produce one warning listing them. Legacy keys (secret, secretFile, flags, cert) are allowed.
• `interval`: the lower bound is unchanged. Values that overflow are refused, with a maximum of 139,748,060 minutes, chosen so the service's +10% random jitter cannot overflow either. Anything above 10080 (one week) is accepted with a warning.
• `baseurl`: a query or fragment is refused, because it breaks requests. A trailing / is trimmed. Credentials in the URL (user:pass@) are accepted with a warning, since they work as Basic auth for reverse proxies. The "configuration loaded" debug log and the per-request debug lines no longer print URL credentials, query or fragment.
• Relative path options log a warning (they resolve against / under systemd and launchd).
• Sample and docs. The sample config drops the /path/to paths (kept as commented examples), so OS defaults apply, and adds osquerySHA256, osqueryPackage and allowUnverified as empty values. The README table and install flags list the same options.
• Secret guidance. The README now recommends a root-only config file or OSCTRL_SECRET, explains the risk of --secret, and clarifies that OSQUERY_SECRET is the secret file path. Nothing was renamed.
• uninstall removes osctrld's own state files and the *.osctrld-upgrade/ directory. It uses Lstat, removes only regular files, and only removes the directory when it is a real directory owned by osctrld. Symlinks are skipped with a warning. Installed extensions are left in place.
Security / operational impact
• Removes a secret-bearing upgrade snapshot from disk on uninstall, and keeps URL credentials out of logs.
• Refusals (exit 2): a config file with no osctrld: section, an interval of 0 or less or above the overflow bound, and a baseurl with a query or fragment.
• New warnings: ignored flags and environment variables, unknown keys, relative paths, credentials in baseurl, and an interval over one week.
• Deliberately not refused: anything an existing config could plausibly contain and that still works at runtime.
• The packaged sample now uses OS default paths.
• An unedited sample still fails validation on the environment placeholder, on purpose, so it cannot sync.
Validation
• go test ./... -count=1 (also with -race), go vet for linux, darwin and windows, gofmt, golangci-lint.
• Tests, each seen failing first except as noted:
• precedence warning with and without a config file;
• missing osctrld: section, which panicked;
• typo and legacy-key detection;
• interval boundaries, and a jitter test at the bound;
• baseurl parts and trailing-slash trimming, including the generated URLs;
• URL credentials accepted, warned once, and never printed;
• the sample config parses and matches the OS defaults, and the keys in the sample, default-config and the README table equal the struct tags;
• the relative-path warning, seen failing only through the real app wiring, because the helper was written just before its test;
• uninstall removes osctrld's files, leaves extensions and unrelated files, and does not follow symlinks. The symlink guard test also passed before the fix.
Not in this PR
• default-config still prints /path/to placeholders.
• Go's HTTP error text still shows a URL's username when a request fails. The password is masked.
• osqueryPackage URLs in packages.go errors and warnings are not redacted.
• Leftover atomic-write temp files and .prev extension files are not removed on uninstall.
• ARCHITECTURE.md (unmerged guidance branch) needs its validation steps, precedence note and uninstall row updated when it lands.
• Windows secret ACL, atomic file writes and the macOS deployment checks.
jmpsec/osctrldGitHub
10/27/2026, 10:38 AM