GitHub
10/23/2026, 4:37 PM--enable-osctrld is a flag of osctrl-tls, and the API did not know it, so every deployment showed these screens even when no osctrld could ever act on them.
What changes
API: /api/v1/features reports whether osctrld is enabled
• FeaturesResponse gains osctrld (bool).
• The API has no osctrld flag and no new config key. It reads the answer from the shared service config table: osctrl-tls writes its running configuration there on every start, and keeps every field the operator has not pinned equal to what the process runs. New (*ServiceConfigManager).OsctrldEnabled() reads the tls / osctrld section (stored with the JSON key Enabled).
• A nil manager, a missing row or malformed JSON returns false and logs at debug level. This is one indexed query per /features call, with no cache.
• OpenAPI is regenerated and only gains the new property.
Frontend: one switch for every osctrld-only surface
• New useOsctrldEnabled() returns { enabled, loaded, failed } from the existing shared ['features'] query. Loading and error both count as not enabled.
• New RequireOsctrld route guard renders nothing while the features request is loading, including its retry, and redirects to the dashboard when osctrld is off, absent or the request failed. It shows the page only when osctrld is enabled.
• Hidden when osctrld is off, with no request made for them:
• the Extensions and Osquery versions admin nav entries, and both pages (they redirect);
• the Extensions and Osquery tabs in environment settings, falling back to the Settings tab if one of them was selected;
• the per-node Flags overrides, Extensions and Osquery version views in the node configuration switcher. Only the plain Configuration view remains, without a switcher that would have a single option;
• on the node page, the flags-override banner, the osctrld-down banner, the extension banners, and the osctrld part of the Configuration tab dot. Their queries are skipped, and the data is forced empty so cached data cannot resurface.
• Deliberately not hidden, because none of it depends on osctrld:
• the environment-level osquery flags configuration (enroll scripts use it);
• the dashboard's osquery versions panel and the per-node osquery version in the node list;
• the plain per-node configuration override;
• the Service Config page, where osctrld is turned on.
Behavior to know about
• The UI follows what osctrl-tls last recorded at startup. If osctrl-tls has never started, or uses a different database than the API, the osctrld UI stays hidden.
• A new frontend against an older API that lacks the field also hides it.
• If an operator edits the osctrld section through the service-config page, the stored value changes immediately but osctrl-tls only applies it after a restart. The Service Config page already has this same window for every section.
• The features data is cached for 5 minutes, so after osctrl-tls restarts with osctrld enabled the entries can take up to that long to appear, or a reload.
• The API routes themselves are unchanged and keep working. Only the UI is gated.
Security and operational impact
• No permission or authorization changes. The routes, handlers and access checks are untouched, and the features endpoint stays behind the same authentication.
• The new field only discloses whether osctrld is enabled, to authenticated users.
Validation
• `go test ./... -count=1`: 58 packages pass, 0 failures
• go vet ./..., gofmt -l pkg cmd (empty), `golangci-lint run ./...`: 0 issues
• `make openapi-check`: clean
• Frontend: 622 unit tests pass and tsc is clean
• New tests, each seen failing first (except those marked as covering behavior that was already correct):
• `OsctrldEnabled()`: enabled, disabled, malformed JSON, an API-service row ignored, a missing row, a nil manager, and a re-seed that flips an unpinned value.
• Features handler: the osctrld key is present with the right value for true, false and no manager.
• Side nav: the entries show for a super admin only when the field is true. They are hidden when it is false or absent, and a non-super-admin never sees them. Other admin entries are unaffected.
• Route guard:
• enabled shows the page;
• false or absent redirects to the dashboard;
• loading shows nothing and does not redirect;
• a failing features request redirects after its retry, with the page never rendered;
• a pending retry shows nothing and does not redirect.
• Environment settings, node configuration, node page: hidden when false, absent or loading, with the API functions asserted not called even with data mocked. The enabled case still shows everything, and the existing tests now mock osctrld: true.
• Dashboard: the versions panel and the environment flags configuration still show without osctrld. This test passes immediately because that code is not gated.
jmpsec/osctrlGitHub
10/23/2026, 4:43 PM