Documentation
¶
Index ¶
- Variables
- func InitOIDC(ctx context.Context, cfg config.YAMLConfigurationOIDC) error
- func InitSAML(ctx context.Context, cfg config.YAMLConfigurationSAML, entityID, acsURL string) error
- type APIQueryData
- type ActivityBucket
- type AuthMethod
- type AuthMethodsResponse
- type AuthProviderRegistry
- func (r *AuthProviderRegistry) AllByType(typ string) []authproviders.ProviderEntry
- func (r *AuthProviderRegistry) AllProviders() []authproviders.ProviderEntry
- func (r *AuthProviderRegistry) Get(id uint) *authproviders.ProviderEntry
- func (r *AuthProviderRegistry) HasOIDC() bool
- func (r *AuthProviderRegistry) HasSAML() bool
- func (r *AuthProviderRegistry) Replace(entries []authproviders.ProviderEntry)
- type ContextKey
- type ContextValue
- type EnvStats
- type ErrorNodeRow
- type FeaturesResponse
- type HandlersApi
- func (h *HandlersApi) ActiveNodesHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) AllNodesHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) AllQueriesShowHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) AllTagsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) AuditLogsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) AuthMethodsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) AuthProvidersApplyHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) AuthProvidersCreateHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) AuthProvidersDeleteHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) AuthProvidersFetchMetadataHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) AuthProvidersGetHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) AuthProvidersListHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) AuthProvidersRevertHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) AuthProvidersTestHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) AuthProvidersTypesHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) AuthProvidersUpdateHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) CarveArchiveHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) CarveListHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) CarveQueriesHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) CarveSamplesHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) CarveShowHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) CarvesActionHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) CarvesRunHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) CheckHandlerAuth(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) CheckHandlerNoAuth(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) ConsoleCommandCreateHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) ConsoleCommandResultsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) ConsoleCommandShowHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) ConsoleSessionCreateHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) ConsoleSessionDeleteHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) ConsoleSessionShowHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) DeleteNodeHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) DeleteUserTokenHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvActionsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvActivityHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvActivityTilesHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvCertUploadHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvConfigurationHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvEnrollActionsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvEnrollHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvErrorNodesHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvPackageAddHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvPackageRemoveHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvPackageUpdateHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvPackagesHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvRemoveActionsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvRemoveHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvironmentConfigHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvironmentConfigPatchHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvironmentCreateHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvironmentDeleteHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvironmentExpirationPatchHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvironmentHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvironmentIntervalsPatchHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvironmentMapHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvironmentUpdateHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) EnvironmentsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) ErrorHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) FeaturesHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) FileExplorerListHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) FileExplorerPrimingResultsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) FileExplorerRequestResultsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) FileExplorerRequestShowHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) FileExplorerSessionCreateHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) FileExplorerSessionDeleteHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) FileExplorerSessionShowHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) FileExplorerStatHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) ForbiddenHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) GetUserPermissionsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) HealthHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) InactiveNodesHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LogSinksApplyHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LogSinksCloneHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LogSinksCreateHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LogSinksDeleteHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LogSinksGetHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LogSinksListHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LogSinksRevertHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LogSinksTypesHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LogSinksUpdateHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LoginEnvironmentsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LoginHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LoginMFAEnrollBeginHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LoginMFAEnrollFinishHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LoginMFAHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LoginMFAWebAuthnBeginHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LoginMFAWebAuthnFinishHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LogoutHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) LookupNodeHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) MFARecoveryHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) MFAStatusHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) MFATOTPBeginHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) MFATOTPDeleteHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) MFATOTPVerifyHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) MFAWebAuthnBeginHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) MFAWebAuthnDeleteHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) MFAWebAuthnFinishHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) MeHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) MePasswordHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) MePatchHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) NodeActivityBatchHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) NodeActivityHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) NodeActivityTilesBatchHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) NodeActivityTilesHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) NodeHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) NodeLogsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) NodePostureHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) NodePostureScoreHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) NodesPagedHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) OIDCCallbackHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) OIDCLoginHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) OsqueryTablesHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) OsqueryVersionsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) PlatformsEnvHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) PlatformsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) PostureProfileHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) PostureProfilesHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) QueriesActionHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) QueriesRunHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) QueryListHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) QueryResultsCSVHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) QueryResultsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) QuerySamplesHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) QueryShowHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) RefreshUserTokenHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) RootHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) SAMLACSHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) SAMLLoginHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) SAMLMetadataHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) SavedQueriesListHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) SavedQueryCreateHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) SavedQueryDeleteHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) SavedQueryUpdateHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) ServiceCommandHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) ServiceConfigApplyHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) ServiceConfigHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) ServiceConfigPersistHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) ServiceConfigSectionHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) ServiceConfigServiceHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) ServiceConfigStatusHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) ServiceConfigUpdateHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) SetUserPermissionsAllHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) SetUserPermissionsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) SettingPatchHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) SettingsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) SettingsServiceEnvHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) SettingsServiceHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) StatsHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) TagEnvHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) TagNodeHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) TagsActionHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) TagsEnvHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) UserActionHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) UserHandler(w http.ResponseWriter, r *http.Request)
- func (h *HandlersApi) UsersHandler(w http.ResponseWriter, r *http.Request)
- type HandlersOption
- func WithActivityReader(ar activityReader) HandlersOption
- func WithAuditLog(auditLog *auditlog.AuditLogManager) HandlersOption
- func WithAuthProviders(reg *AuthProviderRegistry, mgr *authproviders.AuthProviderManager) HandlersOption
- func WithAuthProvidersEnabled(enabled bool) HandlersOption
- func WithCarves(carves *carves.Carves) HandlersOption
- func WithConfigPersist(fn func() error) HandlersOption
- func WithConsole(consoleManager *console.Manager) HandlersOption
- func WithDB(db *gorm.DB) HandlersOption
- func WithDBHealth(hl backend.DegradedReader) HandlersOption
- func WithDebugHTTP(cfg *config.YAMLConfigurationDebug) HandlersOption
- func WithEnvCache(envCache *environments.EnvCache) HandlersOption
- func WithEnvs(envs *environments.EnvManager) HandlersOption
- func WithFileExplorer(fileExplorerManager *fileexplorer.Manager) HandlersOption
- func WithGeoIP(g *geoip.GeoIPResolver) HandlersOption
- func WithJWTSecret(secret []byte) HandlersOption
- func WithLogReader(r logging.LogReader) HandlersOption
- func WithLogSinks(mgr *logsinks.LogSinksManager) HandlersOption
- func WithLogSinksEnabled(enabled bool) HandlersOption
- func WithMFA(manager *mfa.Manager, webAuthn *mfa.WebAuthn, required bool, issuer string) HandlersOption
- func WithName(name string) HandlersOption
- func WithNodes(nodes *nodes.NodeManager) HandlersOption
- func WithOIDC(enabled bool) HandlersOption
- func WithOsqueryTables(tables []types.OsqueryTable) HandlersOption
- func WithOsqueryValues(values config.YAMLConfigurationOsquery) HandlersOption
- func WithPosture(pm *posture.PostureManager) HandlersOption
- func WithPostureEnabled(enabled bool) HandlersOption
- func WithQueries(queries *queries.Queries) HandlersOption
- func WithRestartCh(ch chan<- struct{}) HandlersOption
- func WithSAML(enabled bool) HandlersOption
- func WithServiceCommands(mgr *servicecommands.Manager) HandlersOption
- func WithServiceConfig(mgr *serviceconfig.ServiceConfigManager) HandlersOption
- func WithServiceConfigEnabled(enabled bool) HandlersOption
- func WithSettings(settings *settings.Settings) HandlersOption
- func WithTags(tags *tags.TagManager) HandlersOption
- func WithUsers(users *users.UserManager) HandlersOption
- func WithVersion(version string) HandlersOption
- type LogoutResponse
- type MFAChallengeResponse
- type MFACodeRequest
- type MFACredential
- type MFAEnrollBeginResponse
- type MFAEnrollRequest
- type MFALoginRequest
- type MFAPasswordRequest
- type MFARecoveryCodesResponse
- type MFAStatusResponse
- type MFATOTPBeginResponse
- type MFAWebAuthnRequest
- type NodeActivityBucket
- type NodeLogsResponse
- type StatsResponse
Constants ¶
This section is empty.
Variables ¶
Define targets to be used to retrieve an environment map
var ErrAuthUserRejected = errors.New("auth: identity cannot be resolved to an AdminUser")
ErrAuthUserRejected is returned by resolveFederatedUser when the identity cannot be turned into a usable AdminUser. Callers should not surface this to clients verbatim — return a generic "authentication failed" error to avoid leaking which path rejected (timing-oracle and information-disclosure defense).
var QueryTargets = map[string]bool{ queries.TargetAll: true, queries.TargetAllFull: true, queries.TargetActive: true, queries.TargetCompleted: true, queries.TargetExpired: true, queries.TargetSaved: true, queries.TargetHiddenCompleted: true, queries.TargetDeleted: true, queries.TargetHidden: true, }
QueryTargets enumerates the target filters accepted by QueryListHandler. TargetHiddenActive is intentionally excluded: no UI tab references it and GetByEnvTargetPaged has no branch for it (mirrors Gets() which returns nothing).
Functions ¶
func InitOIDC ¶ added in v0.5.3
func InitOIDC(ctx context.Context, cfg config.YAMLConfigurationOIDC) error
InitOIDC constructs the global OIDC provider for osctrl-api from the YAML/CLI config. Returns a non-nil error on:
- Config.Validate failure (missing issuer, client id, etc.)
- OIDC discovery failure (IdP unreachable, malformed metadata)
The caller (cmd/api/main.go) should treat any error as fatal during startup — we want loud failures so misconfigured deployments don't run silently with federated login broken.
Username sanitization defaults to STRICT for cmd/api (no LegacyPermissiveUsername shim). osctrl-api is greenfield; we don't have pre-existing email-format usernames to preserve.
func InitSAML ¶ added in v0.5.3
InitSAML constructs the global SAML provider for osctrl-api from the YAML/CLI config. Returns a non-nil error on:
- Config.Validate failure (missing metadata, required fields, etc.)
- SAML metadata fetch / parse failure
The caller (cmd/api/main.go) treats any error as fatal during startup — loud failures over silent broken-SSO.
Username sanitization defaults to STRICT for cmd/api (no LegacyPermissiveUsername shim). osctrl-api is greenfield; no pre-existing email-format usernames to preserve.
Types ¶
type ActivityBucket ¶ added in v0.5.2
type ActivityBucket struct {
BucketStart time.Time `json:"bucket_start"`
Config int `json:"config"`
Query int `json:"query"`
Carve int `json:"carve"`
Enroll int `json:"enroll"`
}
ActivityBucket is one cell of the 24-hour activity heatmap. BucketStart is the start of the 15-minute window (UTC, RFC3339); the four counters are the audit-log entry counts that fell into that window for each category.
Categories (audit log_type → category):
- config ← Setting (8) + Environment (7)
- query ← Query (4)
- carve ← Carve (5)
- enroll ← Node (3) — covers enroll, archive, deletion
type AuthMethod ¶ added in v0.5.3
type AuthMethod struct {
Type string `json:"type"`
// LoginURL is the relative URL the SPA should redirect to.
// For federated providers, includes the provider row ID.
LoginURL string `json:"loginUrl"`
// Name is the human-facing label for the provider (e.g.
// "GitHub", "Google", "Corp Keycloak"). Empty for "password".
Name string `json:"name,omitempty"`
// ID is the auth_providers row ID, used by the SPA to build
// the callback URL. 0 for "password".
ID uint `json:"id,omitempty"`
}
AuthMethod describes one auth surface advertised to the SPA.
type AuthMethodsResponse ¶ added in v0.5.3
type AuthMethodsResponse struct {
Methods []AuthMethod `json:"methods"`
}
type AuthProviderRegistry ¶ added in v0.5.7
type AuthProviderRegistry struct {
// contains filtered or unexported fields
}
AuthProviderRegistry holds the live, concurrently-safe set of built auth providers. It replaces the package-global oidcProvider / samlProvider variables with a multi-provider model: the login page renders one button per enabled provider.
The registry is replaced atomically during hot-reload via Replace(). Request handlers read from the registry under a read lock — the swap is invisible to in-flight requests that already resolved their provider pointer.
func NewAuthProviderRegistry ¶ added in v0.5.7
func NewAuthProviderRegistry(entries []authproviders.ProviderEntry) *AuthProviderRegistry
NewAuthProviderRegistry constructs a registry from the given entries.
func (*AuthProviderRegistry) AllByType ¶ added in v0.5.7
func (r *AuthProviderRegistry) AllByType(typ string) []authproviders.ProviderEntry
AllByType returns all enabled providers of the given type ("oidc" or "saml"), sorted by name.
func (*AuthProviderRegistry) AllProviders ¶ added in v0.5.7
func (r *AuthProviderRegistry) AllProviders() []authproviders.ProviderEntry
AllProviders returns metadata for every enabled provider, for the auth methods endpoint.
func (*AuthProviderRegistry) Get ¶ added in v0.5.7
func (r *AuthProviderRegistry) Get(id uint) *authproviders.ProviderEntry
Get returns the provider entry for the given row ID, or nil.
func (*AuthProviderRegistry) HasOIDC ¶ added in v0.5.7
func (r *AuthProviderRegistry) HasOIDC() bool
HasOIDC returns true if at least one OIDC provider is enabled.
func (*AuthProviderRegistry) HasSAML ¶ added in v0.5.7
func (r *AuthProviderRegistry) HasSAML() bool
HasSAML returns true if at least one SAML provider is enabled.
func (*AuthProviderRegistry) Replace ¶ added in v0.5.7
func (r *AuthProviderRegistry) Replace(entries []authproviders.ProviderEntry)
Replace atomically swaps the registry contents. The old providers are not closed (neither OIDC nor SAML providers hold resources that need explicit cleanup at this time — the interface can grow a Close() method later).
type ContextKey ¶
type ContextKey string
ContextKey to help with the context key, to pass session data
type ContextValue ¶
ContextValue to hold session data in the context
type EnvStats ¶ added in v0.5.2
type EnvStats struct {
UUID string `json:"uuid"`
Name string `json:"name"`
Active int64 `json:"active"`
Inactive int64 `json:"inactive"`
Total int64 `json:"total"`
ActiveQueries int `json:"active_queries"`
ActiveCarves int `json:"active_carves"`
// PlatformCounts buckets the env's nodes by OS family (linux / darwin /
// windows / other). Drives the Nodes-table QuickFilters chip row. Counts
// are total (active + inactive), since the filter chip lists all nodes
// of that platform regardless of staleness — the Active/Inactive toggle
// is independent.
PlatformCounts nodes.PlatformCounts `json:"platform_counts"`
}
EnvStats is one row in the per-env breakdown returned by /api/v1/stats.
type ErrorNodeRow ¶ added in v0.5.7
type ErrorNodeRow struct {
UUID string `json:"uuid"`
Hostname string `json:"hostname"`
Errors int64 `json:"errors"`
}
ErrorNodeRow is one entry in the reported-errors drill-down.
type FeaturesResponse ¶ added in v0.5.5
type FeaturesResponse struct {
Posture bool `json:"posture"`
// ServiceConfig gates the whole Service Config section in the SPA. When
// false the /api/v1/service-config routes are not registered at all.
ServiceConfig bool `json:"service_config"`
// LogSinks gates the Log Sinks section in the SPA. Tied to the same
// flag as ServiceConfig — the log_sinks routes are registered
// alongside the service-config routes.
LogSinks bool `json:"log_sinks"`
AuthProviders bool `json:"auth_providers"`
Accelerated bool `json:"accelerated"`
Console bool `json:"console"`
FileExplorer bool `json:"file_explorer"`
}
FeaturesResponse advertises server-side feature switches consumed by the SPA.
type HandlersApi ¶
type HandlersApi struct {
DB *gorm.DB
// LogReader is the read side of the status/result/query log store.
// When the TLS logger writes to S3, this is the S3-backed reader so
// the API can still surface logs to the frontend. When the logger
// writes to the DB, this is the legacy GORM-backed reader. nil falls
// back to h.DB via NewDBLogReader at call time so existing tests
// that only wire WithDB keep working.
LogReader logging.LogReader
Users *users.UserManager
Tags *tags.TagManager
Envs *environments.EnvManager
EnvCache *environments.EnvCache
Nodes *nodes.NodeManager
Queries *queries.Queries
Console *console.Manager
FileExplorer *fileexplorer.Manager
Carves *carves.Carves
Settings *settings.Settings
ServiceConfig *serviceconfig.ServiceConfigManager
// LogSinks manages the log_sinks table when service-config is
// enabled. nil when ServiceConfigEnabled is false — the routes are
// not registered in that case.
LogSinks *logsinks.LogSinksManager
// AuthProviders holds the live multi-provider registry (OIDC + SAML).
AuthProviders *AuthProviderRegistry
AuthProviderMgr *authproviders.AuthProviderManager
ServiceConfigEnabled bool
LogSinksEnabled bool
AuthProvidersEnabled bool
ServiceCommands *servicecommands.Manager
Activity activityReader
GeoIP *geoip.GeoIPResolver
Posture *posture.PostureManager
PostureEnabled bool
// MFA owns the second-factor tables. Nil disables every MFA route.
MFA *mfa.Manager
// WebAuthn is nil when no RP ID or origin could be resolved, which
// leaves TOTP and recovery codes working and hides passkey support.
WebAuthn *mfa.WebAuthn
MFARequired bool
MFAIssuer string
ServiceVersion string
ServiceName string
AuditLog *auditlog.AuditLogManager
ApiConfig *config.APIConfiguration
DebugHTTP *zerolog.Logger
DebugHTTPConfig *config.YAMLConfigurationDebug
OsqueryTables []types.OsqueryTable
OsqueryValues config.YAMLConfigurationOsquery
// JWTSecret is the HMAC key used by pkg/auth state-cookie
// helpers. Populated via WithJWTSecret at handler init. Same
// bytes the Users manager signs user JWTs with; the auth
// state-cookie code uses audience claims to segregate uses.
JWTSecret []byte
// OIDCEnabled is the global toggle for federated login on
// osctrl-api. When true, /api/v1/auth/methods advertises OIDC
// and the OIDC login/callback routes initiate the federated
// flow. When false, only password auth is offered. OIDC is
// global because osctrl-api is a single-tenant API surface;
// per-env identity provider config (if ever needed) would
// belong on the operator layer, not here.
OIDCEnabled bool
// SAMLEnabled is the SAML analogue of OIDCEnabled. Same
// semantics: global, single-tenant, advertised through
// /api/v1/auth/methods. OIDC and SAML can both be on
// simultaneously — the SPA renders one button per advertised
// method.
SAMLEnabled bool
// DBHealth, when wired via WithDBHealth, lets HealthHandler
// report backend degradation. nil means "no monitor" and the
// health endpoint reports healthy as before.
DBHealth backend.DegradedReader
// RestartCh, when wired via WithRestartCh, is signalled by the
// ServiceConfigApplyHandler to trigger a graceful shutdown so the
// process manager restarts the service and picks up DB-edited
// config. nil means the apply endpoint returns 503.
RestartCh chan<- struct{}
// ConfigPersist, when wired via WithConfigPersist, writes this
// service's DB-edited config sections back to its own YAML file. It
// lives in main because the YAML loader does. nil means the persist
// endpoint returns 503 for osctrl-api.
ConfigPersist func() error
}
func CreateHandlersApi ¶
func CreateHandlersApi(opts ...HandlersOption) *HandlersApi
CreateHandlersApi to initialize the Admin handlers struct
func (*HandlersApi) ActiveNodesHandler ¶
func (h *HandlersApi) ActiveNodesHandler(w http.ResponseWriter, r *http.Request)
ActiveNodesHandler - GET Handler for active JSON nodes @Summary List active nodes @Description Returns active enrolled nodes for an environment. @Tags nodes @Produce json @Param env path string true "Environment name or UUID" @Success 200 {array} nodes.OsqueryNode @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/nodes/{env}/active [get]
func (*HandlersApi) AllNodesHandler ¶
func (h *HandlersApi) AllNodesHandler(w http.ResponseWriter, r *http.Request)
AllNodesHandler - GET Handler for all JSON nodes @Summary List all nodes @Description Returns all enrolled nodes for an environment. @Tags nodes @Produce json @Param env path string true "Environment name or UUID" @Success 200 {array} nodes.OsqueryNode @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/nodes/{env}/all [get]
func (*HandlersApi) AllQueriesShowHandler ¶
func (h *HandlersApi) AllQueriesShowHandler(w http.ResponseWriter, r *http.Request)
AllQueriesShowHandler - GET Handler to return all queries in JSON @Summary List queries @Description Returns on-demand queries for an environment. @Tags queries @Produce json @Param env path string true "Environment name or UUID" @Success 200 {array} queries.DistributedQuery @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/queries/{env} [get] @Router /api/v1/all-queries/{env} [get]
func (*HandlersApi) AllTagsHandler ¶
func (h *HandlersApi) AllTagsHandler(w http.ResponseWriter, r *http.Request)
AllTagsHandler - GET Handler for all JSON tags @Summary List tags @Description Returns tags across environments. @Tags tags @Produce json @Success 200 {array} tags.AdminTag @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/tags [get]
func (*HandlersApi) AuditLogsHandler ¶ added in v0.4.8
func (h *HandlersApi) AuditLogsHandler(w http.ResponseWriter, r *http.Request)
AuditLogsHandler - GET /api/v1/audit-logs
Query params:
?service=... exact match on service name ?username=... case-insensitive partial match on username ?type=... log type integer (1..10), see pkg/auditlog.LogType* ?env_uuid=... filter to one environment (resolved to internal ID) ?since=RFC3339 created_at >= since ?until=RFC3339 created_at <= until ?page=N 1-indexed page; default 1 ?page_size=N default 50, max 500
Returns the SPA-canonical paginated envelope. The handler audit-logs the visit on success. @Summary List audit logs @Description Returns paginated API audit log entries. @Tags audit @Produce json @Param page query int false "Page number" @Param page_size query int false "Page size" @Param q query string false "Search query" @Param service query string false "Service filter" @Param username query string false "Username filter" @Param env query string false "Environment filter" @Success 200 {object} types.AuditLogsPagedResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/audit-logs [get]
func (*HandlersApi) AuthMethodsHandler ¶ added in v0.5.3
func (h *HandlersApi) AuthMethodsHandler(w http.ResponseWriter, r *http.Request)
func (*HandlersApi) AuthProvidersApplyHandler ¶ added in v0.5.7
func (h *HandlersApi) AuthProvidersApplyHandler(w http.ResponseWriter, r *http.Request)
AuthProvidersApplyHandler — POST /api/v1/auth-providers/apply
func (*HandlersApi) AuthProvidersCreateHandler ¶ added in v0.5.7
func (h *HandlersApi) AuthProvidersCreateHandler(w http.ResponseWriter, r *http.Request)
AuthProvidersCreateHandler — POST /api/v1/auth-providers
func (*HandlersApi) AuthProvidersDeleteHandler ¶ added in v0.5.7
func (h *HandlersApi) AuthProvidersDeleteHandler(w http.ResponseWriter, r *http.Request)
AuthProvidersDeleteHandler — DELETE /api/v1/auth-providers/{id}
func (*HandlersApi) AuthProvidersFetchMetadataHandler ¶ added in v0.5.7
func (h *HandlersApi) AuthProvidersFetchMetadataHandler(w http.ResponseWriter, r *http.Request)
AuthProvidersFetchMetadataHandler — POST /api/v1/auth-providers/fetch-metadata
Fetches the IdP metadata XML from the given URL and returns it as text. The operator can then review it before saving it into the IDPMetadataXML field of a SAML provider. Saves a round-trip to the IdP's metadata URL from the browser (which may be on a different network than the server) and avoids CORS issues.
@Summary Fetch IdP metadata XML @Description Fetches the SAML IdP metadata document from the given URL and returns the XML. @Tags auth-providers @Accept json @Produce json @Param request body object true "{ \"url\": \"https://idp/metadata\" }" @Success 200 {object} object "{ \"xml\": \"<EntityDescriptor ...>\" }" @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 502 {object} types.ApiErrorResponse "Fetch failed" @Security ApiKeyAuth @Router /api/v1/auth-providers/fetch-metadata [post]
func (*HandlersApi) AuthProvidersGetHandler ¶ added in v0.5.7
func (h *HandlersApi) AuthProvidersGetHandler(w http.ResponseWriter, r *http.Request)
AuthProvidersGetHandler — GET /api/v1/auth-providers/{id}
func (*HandlersApi) AuthProvidersListHandler ¶ added in v0.5.7
func (h *HandlersApi) AuthProvidersListHandler(w http.ResponseWriter, r *http.Request)
AuthProvidersListHandler — GET /api/v1/auth-providers
func (*HandlersApi) AuthProvidersRevertHandler ¶ added in v0.5.7
func (h *HandlersApi) AuthProvidersRevertHandler(w http.ResponseWriter, r *http.Request)
AuthProvidersRevertHandler — POST /api/v1/auth-providers/{id}/revert
func (*HandlersApi) AuthProvidersTestHandler ¶ added in v0.5.7
func (h *HandlersApi) AuthProvidersTestHandler(w http.ResponseWriter, r *http.Request)
AuthProvidersTestHandler — POST /api/v1/auth-providers/test
func (*HandlersApi) AuthProvidersTypesHandler ¶ added in v0.5.7
func (h *HandlersApi) AuthProvidersTypesHandler(w http.ResponseWriter, r *http.Request)
AuthProvidersTypesHandler — GET /api/v1/auth-providers/types
func (*HandlersApi) AuthProvidersUpdateHandler ¶ added in v0.5.7
func (h *HandlersApi) AuthProvidersUpdateHandler(w http.ResponseWriter, r *http.Request)
AuthProvidersUpdateHandler — PUT /api/v1/auth-providers/{id}
func (*HandlersApi) CarveArchiveHandler ¶ added in v0.5.2
func (h *HandlersApi) CarveArchiveHandler(w http.ResponseWriter, r *http.Request)
CarveArchiveHandler - GET /api/v1/carves/{env}/archive/{name}
(The literal `archive` lives in segment 2 — not as a `/{name}/archive` suffix — because Go's ServeMux refuses to register patterns that ambiguously overlap with `/{env}/queries/{target}` registered on the same prefix.)
Streams (or redirects to) the reassembled carve archive blob.
Resolution rules:
- The carve query identified by {name} must exist and be type=carve.
- If exactly one CarvedFile exists for the query, it is served.
- If multiple exist, an explicit ?session=<session-id> must select one. A missing/ambiguous session selector returns 409 Conflict.
- If the underlying file is not yet archived, it is archived on demand (local or DB carver: written to a temp dir, then served; S3: a presigned download URL is returned via 302 redirect).
Content-Disposition is set to attachment with the carve archive filename. @Summary Download carve archive @Description Downloads the archive for a completed file carve. @Tags carves @Produce application/octet-stream @Param env path string true "Environment name or UUID" @Param name path string true "Carve query name" @Success 200 {file} file @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/carves/{env}/archive/{name} [get]
func (*HandlersApi) CarveListHandler ¶
func (h *HandlersApi) CarveListHandler(w http.ResponseWriter, r *http.Request)
CarveListHandler - GET /api/v1/carves/{env}
Paginated, sorted, searchable list of carve queries (DistributedQuery rows with type=carve). Query params: page, page_size, q, sort, dir, target. Empty result → HTTP 200 with items: []. @Summary List file carves @Description Returns paginated file carves for an environment. @Tags carves @Produce json @Param env path string true "Environment name or UUID" @Param page query int false "Page number" @Param page_size query int false "Page size" @Param q query string false "Search query" @Success 200 {object} types.CarvesPagedResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/carves/{env} [get] @Router /api/v1/carves/{env}/list [get]
func (*HandlersApi) CarveQueriesHandler ¶
func (h *HandlersApi) CarveQueriesHandler(w http.ResponseWriter, r *http.Request)
CarveQueriesHandler - GET /api/v1/carves/{env}/queries/{target}
Returns carve queries by target. Retained from the legacy contract; the canonical list endpoint is now CarveListHandler at /api/v1/carves/{env}. @Summary List carve queries @Description Returns file carve queries by target and environment. @Tags carves @Produce json @Param env path string true "Environment name or UUID" @Param target path string true "Carve target filter" @Success 200 {array} queries.DistributedQuery @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/carves/{env}/queries/{target} [get]
func (*HandlersApi) CarveSamplesHandler ¶ added in v0.5.2
func (h *HandlersApi) CarveSamplesHandler(w http.ResponseWriter, r *http.Request)
CarveSamplesHandler - GET /api/v1/carves/samples
Returns the static starter library of common carve-target file paths (e.g., /etc/passwd, C:\Windows\System32\config\SAM). Same auth posture as QuerySamplesHandler. The path list is the set of high-value exfiltration locations osctrl is provisioned to carve; surfacing it to anonymous callers was a free recon gift to attackers. @Summary List carve samples @Description Returns sample carve path templates. @Tags carves @Produce json @Success 200 {array} carves.CarveSample @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/carves/samples [get]
func (*HandlersApi) CarveShowHandler ¶
func (h *HandlersApi) CarveShowHandler(w http.ResponseWriter, r *http.Request)
CarveShowHandler - GET /api/v1/carves/{env}/{name}
Returns the carve query metadata plus the array of per-node CarvedFile rows produced by the carve. Returns 404 when the carve query name does not exist in the environment. @Summary Get file carve @Description Returns a file carve and the files produced by it. @Tags carves @Produce json @Param env path string true "Environment name or UUID" @Param name path string true "Carve query name" @Success 200 {object} types.CarveDetailResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/carves/{env}/{name} [get]
func (*HandlersApi) CarvesActionHandler ¶
func (h *HandlersApi) CarvesActionHandler(w http.ResponseWriter, r *http.Request)
CarvesActionHandler - POST /api/v1/carves/{env}/{action}/{name} @Summary Execute carve action @Description Deletes, expires, or otherwise acts on a file carve. @Tags carves @Accept json @Produce json @Param env path string true "Environment name or UUID" @Param action path string true "Carve action" @Param name path string true "Carve query name" @Success 200 {object} types.ApiGenericResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/carves/{env}/{action}/{name} [post]
func (*HandlersApi) CarvesRunHandler ¶
func (h *HandlersApi) CarvesRunHandler(w http.ResponseWriter, r *http.Request)
CarvesRunHandler - POST /api/v1/carves/{env} @Summary Run file carve @Description Starts a new file carve. @Tags carves @Accept json @Produce json @Param env path string true "Environment name or UUID" @Param request body types.ApiDistributedQueryRequest true "Request body" @Success 200 {object} types.ApiQueriesResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/carves/{env} [post]
func (*HandlersApi) CheckHandlerAuth ¶ added in v0.4.6
func (h *HandlersApi) CheckHandlerAuth(w http.ResponseWriter, r *http.Request)
CheckHandlerAuth - Handle authenticated check requests @Summary Authenticated API check @Description Returns API availability for an authenticated user. @Tags checks @Produce json @Success 200 {string} string @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/checks-auth [get]
func (*HandlersApi) CheckHandlerNoAuth ¶ added in v0.4.6
func (h *HandlersApi) CheckHandlerNoAuth(w http.ResponseWriter, r *http.Request)
CheckHandlerNoAuth - Handle unauthenticated check requests @Summary Unauthenticated API check @Description Returns API availability without requiring authentication. @Tags checks @Produce json @Success 200 {string} string @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Router /api/v1/checks-no-auth [get]
func (*HandlersApi) ConsoleCommandCreateHandler ¶ added in v0.5.5
func (h *HandlersApi) ConsoleCommandCreateHandler(w http.ResponseWriter, r *http.Request)
func (*HandlersApi) ConsoleCommandResultsHandler ¶ added in v0.5.5
func (h *HandlersApi) ConsoleCommandResultsHandler(w http.ResponseWriter, r *http.Request)
func (*HandlersApi) ConsoleCommandShowHandler ¶ added in v0.5.5
func (h *HandlersApi) ConsoleCommandShowHandler(w http.ResponseWriter, r *http.Request)
func (*HandlersApi) ConsoleSessionCreateHandler ¶ added in v0.5.5
func (h *HandlersApi) ConsoleSessionCreateHandler(w http.ResponseWriter, r *http.Request)
func (*HandlersApi) ConsoleSessionDeleteHandler ¶ added in v0.5.5
func (h *HandlersApi) ConsoleSessionDeleteHandler(w http.ResponseWriter, r *http.Request)
func (*HandlersApi) ConsoleSessionShowHandler ¶ added in v0.5.5
func (h *HandlersApi) ConsoleSessionShowHandler(w http.ResponseWriter, r *http.Request)
func (*HandlersApi) DeleteNodeHandler ¶
func (h *HandlersApi) DeleteNodeHandler(w http.ResponseWriter, r *http.Request)
DeleteNodeHandler - POST Handler to delete single node @Summary Delete node @Description Deletes a node from an environment. @Tags nodes @Accept json @Produce json @Param env path string true "Environment name or UUID" @Param request body types.ApiNodeGenericRequest true "Request body" @Success 200 {object} types.ApiGenericResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/nodes/{env}/delete [post]
func (*HandlersApi) DeleteUserTokenHandler ¶ added in v0.5.2
func (h *HandlersApi) DeleteUserTokenHandler(w http.ResponseWriter, r *http.Request)
DeleteUserTokenHandler - DELETE /api/v1/users/{username}/token
Clears the user's APIToken so any existing JWT for them stops working. Requires super-admin OR the user themselves. @Summary Delete user token @Description Deletes an API token for a user. @Tags users @Produce json @Param username path string true "Username" @Success 200 {object} types.ApiGenericResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/users/{username}/token [delete]
func (*HandlersApi) EnvActionsHandler ¶ added in v0.5.2
func (h *HandlersApi) EnvActionsHandler(w http.ResponseWriter, r *http.Request)
EnvActionsHandler - POST Handler to perform actions (create, delete, edit) on environments @Summary Execute environment action @Description Creates or modifies an environment using the legacy action endpoint. @Tags environments @Accept json @Produce json @Param request body types.ApiEnvRequest true "Request body" @Success 200 {object} types.ApiGenericResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/environments/actions [post]
func (*HandlersApi) EnvActivityHandler ¶ added in v0.5.2
func (h *HandlersApi) EnvActivityHandler(w http.ResponseWriter, r *http.Request)
EnvActivityHandler — GET /api/v1/stats/activity/{env}?interval=KEY
Returns audit-log activity for one env over the requested interval, bucketed at a fixed size per interval (see activityIntervalPresets). `interval` accepts 3h / 6h / 12h / 1d / 2d / 3d / 7d (default 1d, falls back to 1d on any unknown value rather than 400ing — the SPA picker is the only allowed source).
Buckets are emitted contiguously — empty windows return zero rows for that bucket — so the SPA can render the grid without densifying client-side. @Summary Get environment activity @Description Returns activity buckets for an environment. @Tags stats @Produce json @Param env path string true "Environment name or UUID" @Param hours query int false "Number of hours to include" @Success 200 {object} ActivityBucket @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/stats/activity/{env} [get]
func (*HandlersApi) EnvActivityTilesHandler ¶ added in v0.5.4
func (h *HandlersApi) EnvActivityTilesHandler(w http.ResponseWriter, r *http.Request)
EnvActivityTilesHandler — GET /api/v1/stats/activity/env-tiles/{env}?days=N
Redis-backed environment-level activity series (same shape as the node variant, aggregated across all nodes in the env).
Returns 503 when the activity store is not configured (Redis unavailable). @Summary Get environment activity tiles @Description Returns Redis-backed hourly activity series for an environment. @Tags stats @Produce json @Param env path string true "Environment name or UUID" @Param days query int false "Days of history (1-7, default 1)" @Success 200 {object} activity.EnvSeries @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/stats/activity/env-tiles/{env} [get]
func (*HandlersApi) EnvCertUploadHandler ¶ added in v0.5.3
func (h *HandlersApi) EnvCertUploadHandler(w http.ResponseWriter, r *http.Request)
EnvCertUploadHandler - POST handler to upload the enrollment certificate for an environment. Body: { "certificate_b64": "<base64 PEM>" }. The PEM must parse as one or more CERTIFICATE blocks and the leaf must be a real x509 cert — we don't accept "looks like base64 of something." Legacy admin's equivalent path skipped this validation; the SPA target gets it so a typo'd paste fails fast instead of breaking enrollment downloads.
func (*HandlersApi) EnvConfigurationHandler ¶ added in v0.5.3
func (h *HandlersApi) EnvConfigurationHandler(w http.ResponseWriter, r *http.Request)
EnvConfigurationHandler - GET handler returning the assembled osquery configuration JSON for an environment. Returns the stored composed blob (options + schedule + packs + decorators + ATC). The composition is kept up to date by RefreshConfiguration, which fires from every parts mutation (UpdateOptions / UpdateSchedule / UpdatePacks / etc. in pkg/environments/osqueryconf.go), so reading the cached value is safe — the agents see the exact same blob.
SECURITY: deliberately a pure read. The first cut of this handler called RefreshConfiguration on every GET, which turned the endpoint into a CSRF-via-GET shape and a hot-loop DB-write hazard when React-Query's stale refetch path hit it. The mutation path on the parts is the canonical place for the recompose.
func (*HandlersApi) EnvEnrollActionsHandler ¶
func (h *HandlersApi) EnvEnrollActionsHandler(w http.ResponseWriter, r *http.Request)
EnvEnrollActionsHandler - POST Handler to perform actions (extend, expire) in enroll values @Summary Execute enrollment action @Description Extends, expires, rotates, or updates enrollment values for an environment. @Tags environments @Accept json @Produce json @Param env path string true "Environment name or UUID" @Param action path string true "Enrollment action" @Param request body types.ApiActionsRequest true "Request body" @Success 200 {object} types.ApiGenericResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/environments/{env}/enroll/{action} [post]
func (*HandlersApi) EnvEnrollHandler ¶
func (h *HandlersApi) EnvEnrollHandler(w http.ResponseWriter, r *http.Request)
EnvEnrollHandler - GET Handler to return node enrollment values (secret, certificate, one-liner) for an environment as JSON @Summary Get enrollment values @Description Returns enrollment helper values for an environment. @Tags environments @Produce json @Param env path string true "Environment name or UUID" @Param target path string true "Enrollment target" @Success 200 {object} types.ApiDataResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/environments/{env}/enroll/{target} [get]
func (*HandlersApi) EnvErrorNodesHandler ¶ added in v0.5.7
func (h *HandlersApi) EnvErrorNodesHandler(w http.ResponseWriter, r *http.Request)
EnvErrorNodesHandler — GET /api/v1/stats/activity/error-nodes/{env}
Returns the nodes reporting the most ERROR-severity status logs in the window, worst first. Backs the dashboard's reported-errors tile drill-down. @Summary Top erroring nodes for an environment @Description Returns nodes with the most ERROR-severity osquery status logs. @Tags stats @Produce json @Param env path string true "Environment name or UUID" @Param days query int false "Days of history (default 1)" @Success 200 {array} ErrorNodeRow @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/stats/activity/error-nodes/{env} [get]
func (*HandlersApi) EnvPackageAddHandler ¶ added in v0.5.6
func (h *HandlersApi) EnvPackageAddHandler(w http.ResponseWriter, r *http.Request)
EnvPackageAddHandler - POST Handler to add an enrolling package @Summary Add enrolling package @Description Adds a new enrolling package to an environment. @Tags environments @Accept json @Produce json @Param env path string true "Environment name or UUID" @Param request body types.ApiAddPackageRequest true "Request body" @Success 201 {object} environments.EnvironmentPackage @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Security ApiKeyAuth @Router /api/v1/environments/{env}/packages [post]
func (*HandlersApi) EnvPackageRemoveHandler ¶ added in v0.5.6
func (h *HandlersApi) EnvPackageRemoveHandler(w http.ResponseWriter, r *http.Request)
EnvPackageRemoveHandler - DELETE Handler to remove an enrolling package @Summary Remove enrolling package @Description Removes an enrolling package from an environment. @Tags environments @Produce json @Param env path string true "Environment name or UUID" @Param id path int true "Package ID" @Success 204 @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Security ApiKeyAuth @Router /api/v1/environments/{env}/packages/{id} [delete]
func (*HandlersApi) EnvPackageUpdateHandler ¶ added in v0.5.6
func (h *HandlersApi) EnvPackageUpdateHandler(w http.ResponseWriter, r *http.Request)
EnvPackageUpdateHandler - PATCH Handler to update a package URL @Summary Update enrolling package URL @Description Updates the URL of an existing enrolling package. @Tags environments @Accept json @Produce json @Param env path string true "Environment name or UUID" @Param id path int true "Package ID" @Param request body types.ApiUpdatePackageRequest true "Request body" @Success 200 {object} environments.EnvironmentPackage @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Security ApiKeyAuth @Router /api/v1/environments/packages/{env}/{id} [patch]
func (*HandlersApi) EnvPackagesHandler ¶ added in v0.5.6
func (h *HandlersApi) EnvPackagesHandler(w http.ResponseWriter, r *http.Request)
EnvPackagesHandler - GET Handler to list all enrolling packages for an environment @Summary List enrolling packages @Description Returns all enrolling packages for an environment. @Tags environments @Produce json @Param env path string true "Environment name or UUID" @Success 200 {array} environments.EnvironmentPackage @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Security ApiKeyAuth @Router /api/v1/environments/{env}/packages [get]
func (*HandlersApi) EnvRemoveActionsHandler ¶
func (h *HandlersApi) EnvRemoveActionsHandler(w http.ResponseWriter, r *http.Request)
EnvRemoveActionsHandler - POST Handler to perform actions (extend, expire) in remove values @Summary Execute removal action @Description Extends, expires, rotates, or updates removal values for an environment. @Tags environments @Accept json @Produce json @Param env path string true "Environment name or UUID" @Param action path string true "Removal action" @Param request body types.ApiActionsRequest true "Request body" @Success 200 {object} types.ApiGenericResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/environments/{env}/remove/{action} [post]
func (*HandlersApi) EnvRemoveHandler ¶
func (h *HandlersApi) EnvRemoveHandler(w http.ResponseWriter, r *http.Request)
EnvRemoveHandler - GET Handler to return node removal values for an environment as JSON @Summary Get removal values @Description Returns removal helper values for an environment. @Tags environments @Produce json @Param env path string true "Environment name or UUID" @Param target path string true "Removal target" @Success 200 {object} types.ApiDataResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/environments/{env}/remove/{target} [get]
func (*HandlersApi) EnvironmentConfigHandler ¶ added in v0.5.2
func (h *HandlersApi) EnvironmentConfigHandler(w http.ResponseWriter, r *http.Request)
EnvironmentConfigHandler - GET /api/v1/environments/config/{env}
Returns the env's JSON-shaped config sections (options/schedule/packs/ decorators/atc/flags) so the SPA's Monaco editor can render each section. @Summary Get environment config @Description Returns raw osquery config sections for an environment. @Tags environments @Produce json @Param env path string true "Environment name or UUID" @Success 200 {object} types.EnvConfigResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/environments/config/{env} [get]
func (*HandlersApi) EnvironmentConfigPatchHandler ¶ added in v0.5.2
func (h *HandlersApi) EnvironmentConfigPatchHandler(w http.ResponseWriter, r *http.Request)
EnvironmentConfigPatchHandler - PATCH /api/v1/environments/config/{env}
Body: optional options/schedule/packs/decorators/atc/flags string fields. Each non-nil field is validated as JSON before persisting; an invalid payload is rejected with 400 (no partial writes). @Summary Update environment config @Description Updates raw osquery config sections for an environment. @Tags environments @Accept json @Produce json @Param env path string true "Environment name or UUID" @Param request body types.EnvConfigPatchRequest true "Request body" @Success 200 {object} types.EnvConfigResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/environments/config/{env} [patch]
func (*HandlersApi) EnvironmentCreateHandler ¶ added in v0.5.2
func (h *HandlersApi) EnvironmentCreateHandler(w http.ResponseWriter, r *http.Request)
EnvironmentCreateHandler - POST /api/v1/environments
Body: { name, hostname, type? }. Generates a UUID, defaults config / schedule / packs / decorators / ATC to "{}", and persists the env. Returns 201 with the created TLSEnvironment. Super-admin only. @Summary Create environment @Description Creates an environment. @Tags environments @Accept json @Produce json @Param request body types.EnvCreateRequest true "Request body" @Success 200 {object} environments.TLSEnvironment @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/environments [post]
func (*HandlersApi) EnvironmentDeleteHandler ¶ added in v0.5.2
func (h *HandlersApi) EnvironmentDeleteHandler(w http.ResponseWriter, r *http.Request)
EnvironmentDeleteHandler - DELETE /api/v1/environments/{env}
Removes the environment. Super-admin only. Returns 200 with a message. @Summary Delete environment @Description Deletes an environment. @Tags environments @Produce json @Param env path string true "Environment name or UUID" @Success 200 {object} types.ApiGenericResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/environments/{env} [delete]
func (*HandlersApi) EnvironmentExpirationPatchHandler ¶ added in v0.5.2
func (h *HandlersApi) EnvironmentExpirationPatchHandler(w http.ResponseWriter, r *http.Request)
EnvironmentExpirationPatchHandler - PATCH /api/v1/environments/expiration/{env}
Convenience wrapper around the existing enrollment lifecycle actions (extend / expire / rotate / not-expire), accepting one of those actions via JSON body instead of as a path segment. Mirrors the legacy EnvEnrollActionsHandler semantics for both enroll and remove paths. @Summary Update environment expiration @Description Updates enrollment expiration state for an environment. @Tags environments @Accept json @Produce json @Param env path string true "Environment name or UUID" @Param request body types.EnvExpirationPatchRequest true "Request body" @Success 200 {object} environments.TLSEnvironment @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/environments/expiration/{env} [patch]
func (*HandlersApi) EnvironmentHandler ¶
func (h *HandlersApi) EnvironmentHandler(w http.ResponseWriter, r *http.Request)
EnvironmentHandler - GET Handler to return one environment by UUID as JSON @Summary Get environment @Description Returns one environment by name or UUID. @Tags environments @Produce json @Param env path string true "Environment name or UUID" @Success 200 {object} types.TLSEnvironmentView @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/environments/{env} [get]
func (*HandlersApi) EnvironmentIntervalsPatchHandler ¶ added in v0.5.2
func (h *HandlersApi) EnvironmentIntervalsPatchHandler(w http.ResponseWriter, r *http.Request)
EnvironmentIntervalsPatchHandler - PATCH /api/v1/environments/intervals/{env}
Body: { config_interval?, log_interval?, query_interval? }. Updates the three node-pull intervals atomically. Unsupplied fields are kept. @Summary Update environment intervals @Description Updates osquery interval settings for an environment. @Tags environments @Accept json @Produce json @Param env path string true "Environment name or UUID" @Param request body types.EnvIntervalsPatchRequest true "Request body" @Success 200 {object} environments.TLSEnvironment @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/environments/intervals/{env} [patch]
func (*HandlersApi) EnvironmentMapHandler ¶
func (h *HandlersApi) EnvironmentMapHandler(w http.ResponseWriter, r *http.Request)
EnvironmentMapHandler - GET Handler to return one environment as JSON @Summary Map environments @Description Returns an environment lookup map by target key. @Tags environments @Produce json @Param target path string true "Map target: id, name, or uuid" @Success 200 {object} map[string]environments.NameUUID @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/environments/map/{target} [get]
func (*HandlersApi) EnvironmentUpdateHandler ¶ added in v0.5.2
func (h *HandlersApi) EnvironmentUpdateHandler(w http.ResponseWriter, r *http.Request)
EnvironmentUpdateHandler - PATCH /api/v1/environments/{env}
Updates name / hostname / type / icon / debug_http / accept_enrolls. Other env fields go through the per-section endpoints. Super-admin only. @Summary Update environment @Description Updates an environment. @Tags environments @Accept json @Produce json @Param env path string true "Environment name or UUID" @Param request body types.EnvUpdateRequest true "Request body" @Success 200 {object} environments.TLSEnvironment @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/environments/{env} [patch]
func (*HandlersApi) EnvironmentsHandler ¶
func (h *HandlersApi) EnvironmentsHandler(w http.ResponseWriter, r *http.Request)
EnvironmentsHandler - GET Handler to return all environments as JSON.
Super-admins see every env; non-super-admins get the subset where their EnvAccess.User OR EnvAccess.Admin is true (the read-surface gate elsewhere in the API). The previous super-admin-only gate meant a non-super-admin user with valid env permissions couldn't even populate the SPA's env switcher — their nav read "No environments configured" even though they had access to envs. @Summary List environments @Description Returns environments visible to the authenticated user. @Tags environments @Produce json @Success 200 {array} types.TLSEnvironmentView @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/environments [get]
func (*HandlersApi) ErrorHandler ¶
func (h *HandlersApi) ErrorHandler(w http.ResponseWriter, r *http.Request)
ErrorHandler - Handle error requests @Summary API error response @Description Returns a generic API error response. @Tags system @Produce json @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {string} string "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Router /error [get]
func (*HandlersApi) FeaturesHandler ¶ added in v0.5.5
func (h *HandlersApi) FeaturesHandler(w http.ResponseWriter, r *http.Request)
FeaturesHandler — GET /api/v1/features.
func (*HandlersApi) FileExplorerListHandler ¶ added in v0.5.5
func (h *HandlersApi) FileExplorerListHandler(w http.ResponseWriter, r *http.Request)
func (*HandlersApi) FileExplorerPrimingResultsHandler ¶ added in v0.5.6
func (h *HandlersApi) FileExplorerPrimingResultsHandler(w http.ResponseWriter, r *http.Request)
FileExplorerPrimingResultsHandler returns the raw osquery_info rows for the session's priming metadata request. Unlike the list/stat results endpoint (which decodes file columns into Entry structs), this returns the generic row maps so the frontend can render osquery_info columns directly.
func (*HandlersApi) FileExplorerRequestResultsHandler ¶ added in v0.5.5
func (h *HandlersApi) FileExplorerRequestResultsHandler(w http.ResponseWriter, r *http.Request)
func (*HandlersApi) FileExplorerRequestShowHandler ¶ added in v0.5.5
func (h *HandlersApi) FileExplorerRequestShowHandler(w http.ResponseWriter, r *http.Request)
func (*HandlersApi) FileExplorerSessionCreateHandler ¶ added in v0.5.5
func (h *HandlersApi) FileExplorerSessionCreateHandler(w http.ResponseWriter, r *http.Request)
func (*HandlersApi) FileExplorerSessionDeleteHandler ¶ added in v0.5.5
func (h *HandlersApi) FileExplorerSessionDeleteHandler(w http.ResponseWriter, r *http.Request)
func (*HandlersApi) FileExplorerSessionShowHandler ¶ added in v0.5.5
func (h *HandlersApi) FileExplorerSessionShowHandler(w http.ResponseWriter, r *http.Request)
func (*HandlersApi) FileExplorerStatHandler ¶ added in v0.5.5
func (h *HandlersApi) FileExplorerStatHandler(w http.ResponseWriter, r *http.Request)
func (*HandlersApi) ForbiddenHandler ¶
func (h *HandlersApi) ForbiddenHandler(w http.ResponseWriter, r *http.Request)
ForbiddenHandler - Handle forbidden error requests @Summary API forbidden response @Description Returns a generic forbidden response. @Tags system @Produce json @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {string} string "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Router /forbidden [get]
func (*HandlersApi) GetUserPermissionsHandler ¶ added in v0.5.3
func (h *HandlersApi) GetUserPermissionsHandler(w http.ResponseWriter, r *http.Request)
GetUserPermissionsHandler - GET /api/v1/users/{username}/permissions
Returns the target user's current permission map: env UUID → {user, query, carve, admin}. Envs with no permission rows are omitted (treated as "no access" by the SPA). Requires super-admin (AdminLevel, NoEnvironment).
Used by the Permissions modal to prefill checkboxes with the user's existing access for the selected env, so the operator sees current state before making changes — no more accidentally overwriting (user:true, query:true) by re-saving the modal's default of (user:true, query:false). @Summary Get user permissions @Description Returns per-environment permissions for a user. @Tags users @Produce json @Param username path string true "Username" @Success 200 {object} types.GetPermissionsResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/users/{username}/permissions [get]
func (*HandlersApi) HealthHandler ¶
func (h *HandlersApi) HealthHandler(w http.ResponseWriter, r *http.Request)
HealthHandler - Handle health requests @Summary API health check @Description Returns the API health response. @Tags system @Produce json @Success 200 {string} string @Success 204 {string} string "Degraded — backend unreachable, serving from cache" @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Router /health [get]
func (*HandlersApi) InactiveNodesHandler ¶
func (h *HandlersApi) InactiveNodesHandler(w http.ResponseWriter, r *http.Request)
InactiveNodesHandler - GET Handler for inactive JSON nodes @Summary List inactive nodes @Description Returns inactive enrolled nodes for an environment. @Tags nodes @Produce json @Param env path string true "Environment name or UUID" @Success 200 {array} nodes.OsqueryNode @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/nodes/{env}/inactive [get]
func (*HandlersApi) LogSinksApplyHandler ¶ added in v0.5.7
func (h *HandlersApi) LogSinksApplyHandler(w http.ResponseWriter, r *http.Request)
LogSinksApplyHandler — POST /api/v1/log-sinks/apply
@Summary Apply log sink changes (hot reload) @Description Queues a reload-log-sinks service command for osctrl-tls. The TLS process rebuilds its per-environment exporter map from the log_sinks table and atomically swaps it in, closing the old sinks. In-flight logs to the old sinks may be dropped during the swap; no restart is required. @Tags log-sinks @Produce json @Success 202 {object} types.ServiceConfigApplyResponse "Reload requested" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 503 {object} types.ApiErrorResponse "Service commands unavailable" @Security ApiKeyAuth @Router /api/v1/log-sinks/apply [post]
func (*HandlersApi) LogSinksCloneHandler ¶ added in v0.5.7
func (h *HandlersApi) LogSinksCloneHandler(w http.ResponseWriter, r *http.Request)
LogSinksCloneHandler — POST /api/v1/log-sinks/clone
@Summary Clone sinks from one environment to another @Description Copies all log sinks from a source environment (use 0 for global) to a target environment. With overwrite=false returns 409 if the target already has sinks. @Tags log-sinks @Accept json @Produce json @Param request body types.LogSinkCloneRequest true "Request body" @Success 200 {array} logSinkDTO @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 409 {object} types.ApiErrorResponse "Target has sinks" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Security ApiKeyAuth @Router /api/v1/log-sinks/clone [post]
func (*HandlersApi) LogSinksCreateHandler ¶ added in v0.5.7
func (h *HandlersApi) LogSinksCreateHandler(w http.ResponseWriter, r *http.Request)
LogSinksCreateHandler — POST /api/v1/log-sinks
@Summary Create a log sink @Description Creates a new log sink instance for the given environment (or global when environment_id is 0). @Tags log-sinks @Accept json @Produce json @Param request body types.LogSinkCreateRequest true "Request body" @Success 201 {object} logSinkDTO @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Security ApiKeyAuth @Router /api/v1/log-sinks [post]
func (*HandlersApi) LogSinksDeleteHandler ¶ added in v0.5.7
func (h *HandlersApi) LogSinksDeleteHandler(w http.ResponseWriter, r *http.Request)
LogSinksDeleteHandler — DELETE /api/v1/log-sinks/{id}
@Summary Delete a log sink @Description Deletes a log sink by ID. Deletion takes effect on the next apply/reload. @Tags log-sinks @Produce json @Param id path int true "Sink ID" @Success 204 "No content" @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Security ApiKeyAuth @Router /api/v1/log-sinks/{id} [delete]
func (*HandlersApi) LogSinksGetHandler ¶ added in v0.5.7
func (h *HandlersApi) LogSinksGetHandler(w http.ResponseWriter, r *http.Request)
LogSinksGetHandler — GET /api/v1/log-sinks/{id}?reveal={0|1}
@Summary Get one log sink @Description Returns a single log sink by ID. Secrets are redacted unless reveal=1 is passed by an admin. @Tags log-sinks @Produce json @Param id path int true "Sink ID" @Param reveal query int false "Pass 1 to include secret fields (admin only)" @Success 200 {object} logSinkDTO @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Security ApiKeyAuth @Router /api/v1/log-sinks/{id} [get]
func (*HandlersApi) LogSinksListHandler ¶ added in v0.5.7
func (h *HandlersApi) LogSinksListHandler(w http.ResponseWriter, r *http.Request)
LogSinksListHandler — GET /api/v1/log-sinks?env={id}&reveal={0|1}
@Summary List log sinks @Description Returns log sink rows, optionally filtered by environment. Secrets are redacted unless reveal=1 is passed by an admin. @Tags log-sinks @Produce json @Param env query int false "Environment ID; omit to list all environments" @Param reveal query int false "Pass 1 to include secret fields in the config (admin only)" @Success 200 {array} logSinkDTO @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Security ApiKeyAuth @Router /api/v1/log-sinks [get]
func (*HandlersApi) LogSinksRevertHandler ¶ added in v0.5.7
func (h *HandlersApi) LogSinksRevertHandler(w http.ResponseWriter, r *http.Request)
LogSinksRevertHandler — POST /api/v1/log-sinks/{id}/revert
Flips a sink row's Source from "db" back to "service" so the next hot-reload re-syncs the config from the current service configuration (flags, env vars, or YAML). The operator clicks "Revert", then "Apply" to trigger the reload — the re-seed overwrites the config with the service-config values before the exporters are rebuilt.
@Summary Revert sink to service config @Description Flips a sink's source back to "service" so the next reload re-syncs the config from the service configuration. No config change happens here — the sync runs in osctrl-tls during the reload. @Tags log-sinks @Produce json @Param id path int true "Sink ID" @Success 200 {object} logSinkDTO @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Security ApiKeyAuth @Router /api/v1/log-sinks/{id}/revert [post]
func (*HandlersApi) LogSinksTypesHandler ¶ added in v0.5.7
func (h *HandlersApi) LogSinksTypesHandler(w http.ResponseWriter, r *http.Request)
LogSinksTypesHandler — GET /api/v1/log-sinks/types
@Summary List supported log sink types @Description Returns the sink type registry: type, description, whether the config carries secrets, and which JSON keys hold them. @Tags log-sinks @Produce json @Success 200 {array} types.LogSinkTypeSpec @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Security ApiKeyAuth @Router /api/v1/log-sinks/types [get]
func (*HandlersApi) LogSinksUpdateHandler ¶ added in v0.5.7
func (h *HandlersApi) LogSinksUpdateHandler(w http.ResponseWriter, r *http.Request)
LogSinksUpdateHandler — PUT /api/v1/log-sinks/{id}
@Summary Update a log sink @Description Replaces an existing log sink's mutable fields. A secret field set to "***" is merged from the previously stored value. @Tags log-sinks @Accept json @Produce json @Param id path int true "Sink ID" @Param request body types.LogSinkUpdateRequest true "Request body" @Success 200 {object} logSinkDTO @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Security ApiKeyAuth @Router /api/v1/log-sinks/{id} [put]
func (*HandlersApi) LoginEnvironmentsHandler ¶ added in v0.5.2
func (h *HandlersApi) LoginEnvironmentsHandler(w http.ResponseWriter, r *http.Request)
LoginEnvironmentsHandler - GET /api/v1/login/environments
Pre-auth endpoint that returns the list of environments the user may attempt to log into. Surface is intentionally minimal: only the env UUID and name. No enroll secrets, no certificates, no settings, no hostnames — those all stay behind auth on /api/v1/environments and its CRUD siblings.
Rationale: forcing the user to type the env name on the login screen is bad UX (you don't know it until you've logged in once, and single-env installs only ever have one option). The legacy admin shows env names pre-auth in its login form, so we're not changing the security posture — just exposing the same identifiers that the URL space already commits to using post-auth.
Like POST /login/{env}, this lives behind the per-IP rate limit registered in main.go so the endpoint can't be turned into an env-enumeration oracle for brute-force prep beyond the limit. @Summary List login environments @Description Returns pre-auth environment choices for the login UI. @Tags auth @Produce json @Success 200 {array} types.LoginEnvironment @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Router /api/v1/login/environments [get]
func (*HandlersApi) LoginHandler ¶
func (h *HandlersApi) LoginHandler(w http.ResponseWriter, r *http.Request)
LoginHandler - POST Handler for API login request
Registered on both POST /api/v1/login and POST /api/v1/login/{env}. When {env} is present, the handler additionally verifies that the user has AdminLevel access to that specific environment before issuing a token. When {env} is absent (the SPA's default), the handler authenticates the user and issues a token without an env-scoped permission check — per-request authorization on every subsequent endpoint enforces env access anyway. @Summary Log in @Description Authenticates an API user and returns a JWT token. @Tags auth @Accept json @Produce json @Param env path string false "Environment name or UUID" @Param request body types.ApiLoginRequest true "Request body" @Success 200 {object} types.ApiLoginResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Router /api/v1/login [post] @Router /api/v1/login/{env} [post]
func (*HandlersApi) LoginMFAEnrollBeginHandler ¶ added in v0.5.6
func (h *HandlersApi) LoginMFAEnrollBeginHandler(w http.ResponseWriter, r *http.Request)
LoginMFAEnrollBeginHandler — POST /api/v1/login/mfa/enroll/begin
Starts TOTP enrollment for a user the deployment requires MFA from. The secret is bound to the challenge rather than stored against the user, so an abandoned enrollment leaves nothing behind. @Summary Begin enrollment during login @Description Returns a TOTP secret for a user who must enroll before logging in. @Tags auth @Accept json @Produce json @Param request body MFAEnrollRequest true "Request body" @Success 200 {object} MFAEnrollBeginResponse @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Router /api/v1/login/mfa/enroll/begin [post]
func (*HandlersApi) LoginMFAEnrollFinishHandler ¶ added in v0.5.6
func (h *HandlersApi) LoginMFAEnrollFinishHandler(w http.ResponseWriter, r *http.Request)
LoginMFAEnrollFinishHandler — POST /api/v1/login/mfa/enroll/finish
Confirms the enrollment with a code from the authenticator, stores the factor, and only then mints the session. Recovery codes come back with it. @Summary Finish enrollment during login @Description Confirms a TOTP enrollment and returns a JWT token plus recovery codes. @Tags auth @Accept json @Produce json @Param request body MFAEnrollRequest true "Request body" @Success 200 {object} types.ApiLoginResponse @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Router /api/v1/login/mfa/enroll/finish [post]
func (*HandlersApi) LoginMFAHandler ¶ added in v0.5.6
func (h *HandlersApi) LoginMFAHandler(w http.ResponseWriter, r *http.Request)
LoginMFAHandler — POST /api/v1/login/mfa
Second step of a password login, answered with a TOTP code or a recovery code. On success it consumes the challenge and mints the session. @Summary Complete a login with a second factor @Description Answers an MFA challenge with a TOTP or recovery code and returns a JWT token. @Tags auth @Accept json @Produce json @Param request body MFALoginRequest true "Request body" @Success 200 {object} types.ApiLoginResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Router /api/v1/login/mfa [post]
func (*HandlersApi) LoginMFAWebAuthnBeginHandler ¶ added in v0.5.6
func (h *HandlersApi) LoginMFAWebAuthnBeginHandler(w http.ResponseWriter, r *http.Request)
LoginMFAWebAuthnBeginHandler — POST /api/v1/login/mfa/webauthn/begin
Returns the assertion options for navigator.credentials.get(). @Summary Begin a WebAuthn login ceremony @Description Returns WebAuthn assertion options for an MFA challenge. @Tags auth @Accept json @Produce json @Param request body MFAWebAuthnRequest true "Request body" @Success 200 {object} map[string]interface{} @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Router /api/v1/login/mfa/webauthn/begin [post]
func (*HandlersApi) LoginMFAWebAuthnFinishHandler ¶ added in v0.5.6
func (h *HandlersApi) LoginMFAWebAuthnFinishHandler(w http.ResponseWriter, r *http.Request)
LoginMFAWebAuthnFinishHandler — POST /api/v1/login/mfa/webauthn/finish
Validates the assertion and mints the session. @Summary Finish a WebAuthn login ceremony @Description Validates a WebAuthn assertion and returns a JWT token. @Tags auth @Accept json @Produce json @Param request body MFAWebAuthnRequest true "Request body" @Success 200 {object} types.ApiLoginResponse @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Router /api/v1/login/mfa/webauthn/finish [post]
func (*HandlersApi) LogoutHandler ¶ added in v0.5.3
func (h *HandlersApi) LogoutHandler(w http.ResponseWriter, r *http.Request)
LogoutHandler — POST /api/v1/logout.
Three-tier teardown:
Clear osctrl_token + osctrl_csrf cookies on the response (Max-Age=0 expires them immediately client-side).
Clear the user's APIToken in the DB so any cached copy of the JWT — including the same JWT in another browser tab — fails the handlerAuthCheck APIToken-match guard on its next request. This is the server-side revocation half: cookie expiry alone is client-honor-system; APIToken=="" turns the token into a no-op even if the bytes are reused.
Return the IdP's end_session_endpoint URL so the SPA can navigate to it. Keycloak's logout page in turn redirects the browser to post_logout_redirect_uri (here, the SPA's /login route). Without this step, the next "Continue with SSO" would silently re-auth against the still-valid IdP session cookie and the user would never see a credential prompt.
Idempotent: a request with no auth context (already-logged-out user) still emits cookie-clearing headers and an end-session URL, returns 200. We do not gate the endpoint behind handlerAuthCheck for this reason — logging out from an expired session should not require a fresh login first.
Username derivation: pulled from the in-memory CSRF cookie pair rather than re-parsing the JWT. The osctrl_token cookie + the stored APIToken match each other in handlerAuthCheck; here we trust the token cookie at face value because the worst case is "someone forged a logout request" — the only consequence is invalidating the legitimate user's token, which is exactly what logout is supposed to do. No privilege gain. @Summary Log out @Description Clears API session cookies and revokes the active token when present. @Tags auth @Accept json @Produce json @Success 200 {object} LogoutResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Router /api/v1/logout [post]
func (*HandlersApi) LookupNodeHandler ¶ added in v0.4.5
func (h *HandlersApi) LookupNodeHandler(w http.ResponseWriter, r *http.Request)
LookupNodeHandler - POST Handler to lookup a node by identifier @Summary Lookup node @Description Looks up a node by UUID, hostname, or local name. @Tags nodes @Accept json @Produce json @Param request body types.ApiLookupRequest true "Request body" @Success 200 {object} nodes.OsqueryNode @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/nodes/lookup [post]
func (*HandlersApi) MFARecoveryHandler ¶ added in v0.5.6
func (h *HandlersApi) MFARecoveryHandler(w http.ResponseWriter, r *http.Request)
MFARecoveryHandler — POST /api/v1/mfa/recovery
Replaces the recovery codes and returns the new set. @Summary Regenerate recovery codes @Description Invalidates the existing recovery codes and returns a new set. @Tags mfa @Accept json @Produce json @Param request body MFAPasswordRequest true "Request body" @Success 200 {object} MFARecoveryCodesResponse @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Router /api/v1/mfa/recovery [post]
func (*HandlersApi) MFAStatusHandler ¶ added in v0.5.6
func (h *HandlersApi) MFAStatusHandler(w http.ResponseWriter, r *http.Request)
MFAStatusHandler — GET /api/v1/mfa @Summary Multi-factor status @Description Returns the second factors enrolled on the calling account. @Tags mfa @Produce json @Success 200 {object} MFAStatusResponse @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Router /api/v1/mfa [get]
func (*HandlersApi) MFATOTPBeginHandler ¶ added in v0.5.6
func (h *HandlersApi) MFATOTPBeginHandler(w http.ResponseWriter, r *http.Request)
MFATOTPBeginHandler — POST /api/v1/mfa/totp
Starts enrollment and returns the shared secret. Nothing is active until the verify step, so an abandoned QR code changes nothing. @Summary Begin TOTP enrollment @Description Generates a TOTP secret and provisioning URI for the calling account. @Tags mfa @Produce json @Success 200 {object} MFATOTPBeginResponse @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Router /api/v1/mfa/totp [post]
func (*HandlersApi) MFATOTPDeleteHandler ¶ added in v0.5.6
func (h *HandlersApi) MFATOTPDeleteHandler(w http.ResponseWriter, r *http.Request)
MFATOTPDeleteHandler — DELETE /api/v1/mfa/totp
Removes the authenticator factor. Refused when the deployment requires MFA and this is the account's last factor: dropping it would lock the user out on their next login. @Summary Remove TOTP @Description Disables the authenticator-app factor after confirming the account password. @Tags mfa @Accept json @Produce json @Param request body MFAPasswordRequest true "Request body" @Success 200 {object} types.ApiGenericResponse @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Router /api/v1/mfa/totp [delete]
func (*HandlersApi) MFATOTPVerifyHandler ¶ added in v0.5.6
func (h *HandlersApi) MFATOTPVerifyHandler(w http.ResponseWriter, r *http.Request)
MFATOTPVerifyHandler — POST /api/v1/mfa/totp/verify
Confirms enrollment and hands out recovery codes. @Summary Confirm TOTP enrollment @Description Verifies a code from the authenticator app and returns recovery codes. @Tags mfa @Accept json @Produce json @Param request body MFACodeRequest true "Request body" @Success 200 {object} MFARecoveryCodesResponse @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Router /api/v1/mfa/totp/verify [post]
func (*HandlersApi) MFAWebAuthnBeginHandler ¶ added in v0.5.6
func (h *HandlersApi) MFAWebAuthnBeginHandler(w http.ResponseWriter, r *http.Request)
MFAWebAuthnBeginHandler — POST /api/v1/mfa/webauthn
Starts registration of a passkey or security key for the calling user. @Summary Begin WebAuthn registration @Description Returns WebAuthn creation options for the calling account. @Tags mfa @Produce json @Success 200 {object} map[string]interface{} @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Router /api/v1/mfa/webauthn [post]
func (*HandlersApi) MFAWebAuthnDeleteHandler ¶ added in v0.5.6
func (h *HandlersApi) MFAWebAuthnDeleteHandler(w http.ResponseWriter, r *http.Request)
MFAWebAuthnDeleteHandler — DELETE /api/v1/mfa/webauthn/{id} @Summary Remove a WebAuthn credential @Description Deletes one registered passkey or security key after confirming the account password. @Tags mfa @Accept json @Produce json @Param id path string true "Credential ID" @Param request body MFAPasswordRequest true "Request body" @Success 200 {object} types.ApiGenericResponse @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Router /api/v1/mfa/webauthn/{id} [delete]
func (*HandlersApi) MFAWebAuthnFinishHandler ¶ added in v0.5.6
func (h *HandlersApi) MFAWebAuthnFinishHandler(w http.ResponseWriter, r *http.Request)
MFAWebAuthnFinishHandler — POST /api/v1/mfa/webauthn/verify
Validates the attestation and stores the credential. @Summary Finish WebAuthn registration @Description Validates a WebAuthn attestation and stores the credential. @Tags mfa @Accept json @Produce json @Param request body MFAWebAuthnRequest true "Request body" @Success 200 {object} types.ApiGenericResponse @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Router /api/v1/mfa/webauthn/verify [post]
func (*HandlersApi) MeHandler ¶ added in v0.5.2
func (h *HandlersApi) MeHandler(w http.ResponseWriter, r *http.Request)
MeHandler - GET /api/v1/users/me
Returns the currently authenticated user's profile (sans password hash and API token). Useful for the SPA's Profile page. @Summary Get current user @Description Returns the currently authenticated user profile. @Tags users @Produce json @Success 200 {object} types.UserMeResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/users/me [get]
func (*HandlersApi) MePasswordHandler ¶ added in v0.5.2
func (h *HandlersApi) MePasswordHandler(w http.ResponseWriter, r *http.Request)
MePasswordHandler - POST /api/v1/users/me/password
Changes the currently authenticated user's password. Verifies the current password (bcrypt) before persisting the new hash. @Summary Change current user password @Description Changes the current user's password. @Tags users @Accept json @Produce json @Param request body types.PasswordChangeRequest true "Request body" @Success 200 {object} types.ApiGenericResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/users/me/password [post]
func (*HandlersApi) MePatchHandler ¶ added in v0.5.2
func (h *HandlersApi) MePatchHandler(w http.ResponseWriter, r *http.Request)
MePatchHandler - PATCH /api/v1/users/me
Updates email and/or fullname for the currently authenticated user. Sends each empty field through unchanged. Returns the updated profile. @Summary Update current user @Description Updates the current user's profile fields. @Tags users @Accept json @Produce json @Param request body types.UserMePatchRequest true "Request body" @Success 200 {object} types.UserMeResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/users/me [patch]
func (*HandlersApi) NodeActivityBatchHandler ¶ added in v0.5.2
func (h *HandlersApi) NodeActivityBatchHandler(w http.ResponseWriter, r *http.Request)
NodeActivityBatchHandler — GET /api/v1/stats/activity/node-batch/{env}?uuids=A,B,C&interval=KEY
Returns activity buckets for up to 100 nodes in one call. The response is a map keyed by node UUID so the SPA can render a sparkline per row in the Nodes table without firing N parallel requests.
Cap is 100 to bound the per-request DB load — each node still requires 4 timestamp queries. The SPA's pagination is already <=500 page size; for pages above 100 nodes the SPA fans out 2-3 batch requests instead.
Unknown / unauthorized UUIDs are silently omitted from the response (they're treated as "no data"), not 404'd — that lets a single bad UUID in the list not break the whole page render. @Summary Get node activity batch @Description Returns activity buckets for multiple nodes in an environment. @Tags stats @Produce json @Param env path string true "Environment name or UUID" @Param uuids query string false "Comma-separated node UUIDs" @Param hours query int false "Number of hours to include" @Success 200 {object} map[string][]NodeActivityBucket @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/stats/activity/node-batch/{env} [get]
func (*HandlersApi) NodeActivityHandler ¶ added in v0.5.2
func (h *HandlersApi) NodeActivityHandler(w http.ResponseWriter, r *http.Request)
NodeActivityHandler — GET /api/v1/stats/activity/node/{env}/{uuid}?interval=KEY
Per-node version of EnvActivityHandler. Same bucketing rules (see activityIntervalPresets). The four categories partition different DB tables (see NodeActivityBucket) keyed by the node's UUID — except node_queries which keys by numeric NodeID, looked up once from the resolved node. @Summary Get node activity @Description Returns activity buckets for a node. @Tags stats @Produce json @Param env path string true "Environment name or UUID" @Param uuid path string true "Node UUID" @Param hours query int false "Number of hours to include" @Success 200 {object} NodeActivityBucket @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/stats/activity/node/{env}/{uuid} [get]
func (*HandlersApi) NodeActivityTilesBatchHandler ¶ added in v0.5.5
func (h *HandlersApi) NodeActivityTilesBatchHandler(w http.ResponseWriter, r *http.Request)
NodeActivityTilesBatchHandler — GET /api/v1/stats/activity/node-tiles-batch/{env}?uuids=A,B,C&days=N
Batch version of NodeActivityTilesHandler: returns Redis-backed per-node activity tile series for up to 100 nodes in one call. The response is a map keyed by node UUID so the SPA can render a per-row heatmap in the Nodes table without firing N parallel requests.
Same 503 when Redis is not configured, same day-count clamping as the single-node variant. Unknown / unauthorized UUIDs are silently omitted. @Summary Get node activity tiles batch @Description Returns Redis-backed hourly activity series for multiple nodes. @Tags stats @Produce json @Param env path string true "Environment name or UUID" @Param uuids query string false "Comma-separated node UUIDs" @Param days query int false "Days of history (1-7, default 1)" @Success 200 {object} map[string]activity.NodeTileSeries @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/stats/activity/node-tiles-batch/{env} [get]
func (*HandlersApi) NodeActivityTilesHandler ¶ added in v0.5.4
func (h *HandlersApi) NodeActivityTilesHandler(w http.ResponseWriter, r *http.Request)
NodeActivityTilesHandler — GET /api/v1/stats/activity/node-tiles/{env}/{uuid}?days=N
Returns the Redis-backed per-node activity series: hourly counters for enroll / config / status / result / query_read / query_write / total over the last N days (default 1, capped at retention). This is the finer-grained counterpart to the DB-backed NodeActivityHandler: it carries the config and read/write split the DB buckets collapse into a single "query" category, so the SPA can show per-endpoint last-seen activity.
Returns 503 when the activity store is not configured (Redis unavailable). @Summary Get per-node activity tiles @Description Returns Redis-backed hourly activity series for a node. @Tags stats @Produce json @Param env path string true "Environment name or UUID" @Param uuid path string true "Node UUID" @Param days query int false "Days of history (1-7, default 1)" @Success 200 {object} activity.NodeTileSeries @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/stats/activity/node-tiles/{env}/{uuid} [get]
func (*HandlersApi) NodeHandler ¶
func (h *HandlersApi) NodeHandler(w http.ResponseWriter, r *http.Request)
NodeHandler - GET Handler for single JSON nodes @Summary Get node @Description Returns a single enrolled node in an environment, including admin-only detail fields. @Tags nodes @Produce json @Param env path string true "Environment name or UUID" @Param node path string true "Node UUID, hostname, or local name" @Success 200 {object} types.NodeView @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/nodes/{env}/node/{node} [get]
func (*HandlersApi) NodeLogsHandler ¶ added in v0.5.2
func (h *HandlersApi) NodeLogsHandler(w http.ResponseWriter, r *http.Request)
NodeLogsHandler returns recent log entries for a node.
Path: /api/v1/logs/{type}/{env}/{uuid}
type: "status" | "result" env: UUID or name uuid: node UUID
Query params:
since: RFC3339 timestamp; entries strictly after this point only limit: 1..1000 (default 100)
@Summary Get node logs @Description Returns recent status or result logs for a node. @Tags logs @Produce json @Param env path string true "Environment name or UUID" @Param type path string true "Log type: status or result" @Param uuid path string true "Node UUID" @Param limit query int false "Maximum number of log rows" @Param since query string false "RFC3339 lower bound" @Param q query string false "Free-text search (substring, case-insensitive)" @Param severity query string false "Severity filter for status logs (0=info, 1=warning, 2=error)" @Success 200 {object} NodeLogsResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/logs/{type}/{env}/{uuid} [get]
func (*HandlersApi) NodePostureHandler ¶ added in v0.5.5
func (h *HandlersApi) NodePostureHandler(w http.ResponseWriter, r *http.Request)
NodePostureHandler — GET /api/v1/nodes/{env}/node/{uuid}/posture
Returns all posture categories for a node (latest snapshot per category). @Summary Get node posture @Description Returns security and compliance posture data for a node. @Tags nodes @Produce json @Param env path string true "Environment name or UUID" @Param uuid path string true "Node UUID" @Success 200 {array} posture.NodePosture @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Security ApiKeyAuth @Router /api/v1/nodes/{env}/node/{uuid}/posture [get]
func (*HandlersApi) NodePostureScoreHandler ¶ added in v0.5.5
func (h *HandlersApi) NodePostureScoreHandler(w http.ResponseWriter, r *http.Request)
NodePostureScoreHandler — GET /api/v1/nodes/{env}/node/{uuid}/posture/score
Evaluates the node's posture data against SOC2/ISO 27001 controls and returns a quantified risk score. @Summary Get node posture risk score @Description Returns a security and compliance risk score based on posture data. @Tags nodes @Produce json @Param env path string true "Environment name or UUID" @Param uuid path string true "Node UUID" @Success 200 {object} posture.PostureScore @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/nodes/{env}/node/{uuid}/posture/score [get]
func (*HandlersApi) NodesPagedHandler ¶ added in v0.5.2
func (h *HandlersApi) NodesPagedHandler(w http.ResponseWriter, r *http.Request)
NodesPagedHandler returns paginated, sorted, searchable nodes for an env. This is the canonical endpoint consumed by the React admin SPA.
Query params:
status: "all" | "active" | "inactive" (default "all")
q: free-text search (case-insensitive partial match on uuid,
hostname, localname, ip, username, osquery_user, platform, version)
sort: one of nodes.SortableColumns keys (default "lastseen")
dir: "asc" | "desc" (default "desc" for lastseen, "asc" otherwise)
page: 1-indexed page number (default 1)
page_size: 1..500 (default 50)
@Summary List paginated nodes @Description Returns paginated, filtered, and sorted nodes for an environment. @Tags nodes @Produce json @Param env path string true "Environment name or UUID" @Param page query int false "Page number" @Param page_size query int false "Page size" @Param q query string false "Search query" @Param status query string false "Node status filter" @Param platform query string false "Platform filter" @Param sort query string false "Sort field" @Param order query string false "Sort order" @Success 200 {object} types.NodesPagedResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/nodes/{env} [get]
func (*HandlersApi) OIDCCallbackHandler ¶ added in v0.5.3
func (h *HandlersApi) OIDCCallbackHandler(w http.ResponseWriter, r *http.Request)
OIDCCallbackHandler — GET /api/v1/auth/oidc/callback.
Consumes the IdP's callback URL:
- Parse + verify the state JWT cookie (audience, expiry, signature).
- Clear the state cookie immediately — single-use semantics.
- Run Provider.HandleCallback (the full 10-step verification chain including signature, iss, aud, exp, nonce, group gate, username sanitization).
- Resolve the identity to an AdminUser (existing row or JIT provision per oidcJITProvision).
- Mint user JWT + CSRF cookies via userJWTSessionTokens.
- 302-redirect to "/" — the SPA root takes over from there.
All failure paths redirect to "/" too (no error param leak). The server-side log records WHY; the client gets a generic outcome. This is the timing-oracle defense (threat T31): every failure mode produces an indistinguishable client-visible response. @Summary Complete OIDC login @Description Handles the OIDC authorization callback and creates an API session. @Tags auth @Produce json @Param code query string false "OIDC authorization code" @Param state query string false "OIDC state" @Success 200 {string} string @Failure 302 {string} string @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Router /api/v1/auth/oidc/callback [get]
func (*HandlersApi) OIDCLoginHandler ¶ added in v0.5.3
func (h *HandlersApi) OIDCLoginHandler(w http.ResponseWriter, r *http.Request)
OIDCLoginHandler — GET /api/v1/auth/oidc/login.
Starts the Authorization Code flow:
- Mint a fresh 256-bit nonce (and, if PKCE is enabled, a verifier).
- Issue the state JWT cookie (Path=/api/v1/auth/, HttpOnly, Secure, SameSite=Lax, 10-minute TTL).
- Build the IdP authorize URL with state=<nonce>, nonce=<nonce>, and (when PKCE on) code_challenge=S256(verifier).
- 302-redirect the browser to the IdP.
Unauthenticated: the user is logging IN, so they cannot have a session yet. The state cookie is the only thing tying the eventual callback back to this request.
EnvUUID on the State is a fixed sentinel ("api") because osctrl-api has no per-env IdP concept — see auth-providers spec § "OIDC is global." The legacy admin uses "admin" for the same reason. @Summary Start OIDC login @Description Redirects the browser to the configured OIDC identity provider. @Tags auth @Produce json @Success 200 {string} string @Failure 302 {string} string @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Router /api/v1/auth/oidc/login [get]
func (*HandlersApi) OsqueryTablesHandler ¶ added in v0.5.2
func (h *HandlersApi) OsqueryTablesHandler(w http.ResponseWriter, r *http.Request)
OsqueryTablesHandler - GET Handler to return the osquery schema tables
Path: /api/v1/osquery/tables The schema is global (not env-scoped). Requires any authenticated user. Responses are cache-able for one hour since the schema rarely changes. @Summary List osquery tables @Description Returns the osquery schema table metadata known to the API. @Tags osquery @Produce json @Success 200 {array} types.OsqueryTable @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/osquery/tables [get]
func (*HandlersApi) OsqueryVersionsHandler ¶ added in v0.5.2
func (h *HandlersApi) OsqueryVersionsHandler(w http.ResponseWriter, r *http.Request)
OsqueryVersionsHandler — GET /api/v1/stats/osquery-versions.
Returns fleet-wide osquery agent version breakdown for the dashboard's "fleet hygiene" panel. Operators use this to spot stale agents that need upgrading. Cross-env (no env filter); the dashboard already surfaces the per-env breakdown in its env tiles.
Counts include both active and inactive nodes — a node sitting at an old osquery version is still "stale" even if it's offline today, because once it comes back online it'll come back stale. @Summary Get osquery version stats @Description Returns fleet-wide osquery version counts. @Tags stats @Produce json @Success 200 {object} nodes.OsqueryVersionCount @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/stats/osquery-versions [get]
func (*HandlersApi) PlatformsEnvHandler ¶
func (h *HandlersApi) PlatformsEnvHandler(w http.ResponseWriter, r *http.Request)
PlatformsEnvHandler - GET Handler to return platforms for one environment as JSON @Summary List platforms @Description Returns platform counts for an environment. @Tags platforms @Produce json @Param env path string true "Environment name or UUID" @Success 200 {array} nodes.PlatformCounts @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/platforms/{env} [get]
func (*HandlersApi) PlatformsHandler ¶
func (h *HandlersApi) PlatformsHandler(w http.ResponseWriter, r *http.Request)
PlatformsHandler - GET Handler for multiple JSON platforms
func (*HandlersApi) PostureProfileHandler ¶ added in v0.5.5
func (h *HandlersApi) PostureProfileHandler(w http.ResponseWriter, r *http.Request)
PostureProfileHandler — GET /api/v1/posture/profiles/{id}
Returns a single posture profile by ID. @Summary Get posture profile @Description Returns a single posture check profile by ID. @Tags posture @Produce json @Param id path string true "Profile ID" @Success 200 {object} posture.PostureProfile @Failure 404 {object} types.ApiErrorResponse "Not found" @Security ApiKeyAuth @Router /api/v1/posture/profiles/{id} [get]
func (*HandlersApi) PostureProfilesHandler ¶ added in v0.5.5
func (h *HandlersApi) PostureProfilesHandler(w http.ResponseWriter, r *http.Request)
PostureProfilesHandler — GET /api/v1/posture/profiles
Returns all predefined posture check profiles that operators can merge into an environment's schedule config. @Summary List posture profiles @Description Returns predefined posture check profiles for different node types. @Tags posture @Produce json @Success 200 {array} posture.PostureProfile @Security ApiKeyAuth @Router /api/v1/posture/profiles [get]
func (*HandlersApi) QueriesActionHandler ¶
func (h *HandlersApi) QueriesActionHandler(w http.ResponseWriter, r *http.Request)
QueriesActionHandler - POST Handler to delete/expire a query @Summary Execute query action @Description Deletes, expires, or otherwise acts on an on-demand query. @Tags queries @Accept json @Produce json @Param env path string true "Environment name or UUID" @Param action path string true "Query action" @Param name path string true "Query name" @Success 200 {object} types.ApiGenericResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/queries/{env}/{action}/{name} [post]
func (*HandlersApi) QueriesRunHandler ¶
func (h *HandlersApi) QueriesRunHandler(w http.ResponseWriter, r *http.Request)
QueriesRunHandler - POST Handler to run a query @Summary Run query @Description Starts a new distributed query. @Tags queries @Accept json @Produce json @Param env path string true "Environment name or UUID" @Param request body types.ApiDistributedQueryRequest true "Request body" @Success 200 {object} types.ApiQueriesResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/queries/{env} [post]
func (*HandlersApi) QueryListHandler ¶
func (h *HandlersApi) QueryListHandler(w http.ResponseWriter, r *http.Request)
QueryListHandler - GET Handler to return queries in JSON by target and environment (paginated)
Query params: page, page_size, q (free-text search), sort (column key), dir (asc|desc) @Summary List paginated queries @Description Returns paginated on-demand queries by target and environment. @Tags queries @Produce json @Param env path string true "Environment name or UUID" @Param target path string true "Query target filter" @Param page query int false "Page number" @Param page_size query int false "Page size" @Param q query string false "Search query" @Param sort query string false "Sort field" @Param order query string false "Sort order" @Success 200 {object} types.QueriesPagedResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/queries/{env}/list/{target} [get]
func (*HandlersApi) QueryResultsCSVHandler ¶ added in v0.5.2
func (h *HandlersApi) QueryResultsCSVHandler(w http.ResponseWriter, r *http.Request)
QueryResultsCSVHandler - GET Handler to stream query results as CSV
Path: /api/v1/queries/{env}/results/csv/{name}
(The `.csv` lives as a literal path segment before `{name}` because Go's ServeMux grammar requires wildcards to end at `/` or end-of-pattern, so `{name}.csv` is a parse error at registration time.) @Summary Export query results CSV @Description Streams query results as CSV. @Tags queries @Produce text/csv @Param env path string true "Environment name or UUID" @Param name path string true "Query name" @Success 200 {string} string @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/queries/{env}/results/csv/{name} [get]
func (*HandlersApi) QueryResultsHandler ¶
func (h *HandlersApi) QueryResultsHandler(w http.ResponseWriter, r *http.Request)
QueryResultsHandler - GET Handler to return paginated query results in JSON
Path: /api/v1/queries/{env}/results/{name} Params: page, page_size, since (RFC3339 timestamp; unparseable → ignored)
Empty results are a valid state and return HTTP 200 with items: []. @Summary Get query results @Description Returns paginated results for an on-demand query. @Tags queries @Produce json @Param env path string true "Environment name or UUID" @Param name path string true "Query name" @Param page query int false "Page number" @Param page_size query int false "Page size" @Param since query string false "RFC3339 lower bound" @Success 200 {object} types.QueryResultsResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/queries/{env}/results/{name} [get]
func (*HandlersApi) QuerySamplesHandler ¶ added in v0.5.2
func (h *HandlersApi) QuerySamplesHandler(w http.ResponseWriter, r *http.Request)
QuerySamplesHandler - GET /api/v1/queries/samples
Returns the static starter library of osquery SQL templates so the SPA's queries/new form can populate its QuickTemplates row. Authenticated.
History: an earlier revision exposed this pre-auth on the rationale that the data is static and ships with the binary. Even read-only data fingerprints the deployment as osctrl and reveals the SQL-template starter pack to anonymous callers — neither of which the only consumer (the post-login queries/new form) requires at pre-auth time. Moved behind handlerAuthCheck in cmd/api/main.go. @Summary List query samples @Description Returns sample query templates. @Tags queries @Produce json @Success 200 {array} queries.QuerySample @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/queries/samples [get]
func (*HandlersApi) QueryShowHandler ¶
func (h *HandlersApi) QueryShowHandler(w http.ResponseWriter, r *http.Request)
QueryShowHandler - GET Handler to return a single query in JSON @Summary Get query @Description Returns a single on-demand query. @Tags queries @Produce json @Param env path string true "Environment name or UUID" @Param name path string true "Query name" @Success 200 {object} queries.DistributedQuery @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/queries/{env}/{name} [get]
func (*HandlersApi) RefreshUserTokenHandler ¶ added in v0.5.2
func (h *HandlersApi) RefreshUserTokenHandler(w http.ResponseWriter, r *http.Request)
RefreshUserTokenHandler - POST /api/v1/users/{username}/token/refresh
Generates a new JWT for the target user, persists it as the user's APIToken (invalidating the previous token), and returns the new token + expiry. Requires super-admin OR the request author asking for their own token. Audit-logged on success. @Summary Refresh user token @Description Refreshes an API token for a user. @Tags users @Accept json @Produce json @Param username path string true "Username" @Success 200 {object} types.TokenResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/users/{username}/token/refresh [post]
func (*HandlersApi) RootHandler ¶
func (h *HandlersApi) RootHandler(w http.ResponseWriter, r *http.Request)
RootHandler - Handle root requests.
`GET /` is registered as the api's liveness check (returns "✅" with 200). Go's ServeMux uses `/` as a wildcard match for any GET request the mux doesn't have a more-specific pattern for, which means typos like `GET /api/v1/totally-fake-endpoint` would land here and return 200 — confusing for clients debugging an integration. We tighten the contract: respond 200 ONLY when the request actually targets `/`. Other GETs that fall through fall out as 404.
Returning 404 here doesn't leak endpoint structure beyond what's already in the public OpenAPI; it just stops the api from silently claiming success on misrouted requests. @Summary API root liveness check @Description Returns the API root liveness response. @Tags system @Produce json @Success 200 {string} string @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Router / [get]
func (*HandlersApi) SAMLACSHandler ¶ added in v0.5.3
func (h *HandlersApi) SAMLACSHandler(w http.ResponseWriter, r *http.Request)
SAMLACSHandler — POST /api/v1/auth/saml/acs.
Consumes the IdP's signed SAMLResponse POST:
- Parse + verify the state JWT cookie (audience, expiry, signature).
- Clear the state cookie immediately — single-use semantics.
- Run Provider.HandleCallback (RelayState match, signature, audience, NotBefore/NotOnOrAfter, recipient, InResponseTo, replay cache, groups gate, username sanitization).
- Resolve the identity to an AdminUser (existing row or JIT provision per samlJITProvision).
- Mint user JWT + CSRF cookies via userJWTSessionTokens.
- 302-redirect to "/" — the SPA root takes over.
Failure paths redirect to "/" too (no error param leak). Server-side log records WHY; client sees a generic outcome. Timing-oracle defense matches the OIDC handler. @Summary Complete SAML login @Description Handles the SAML assertion consumer service callback and creates an API session. @Tags auth @Accept json @Produce json @Param SAMLResponse formData string false "SAML response assertion" @Param RelayState formData string false "SAML relay state" @Success 200 {string} string @Failure 302 {string} string @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Router /api/v1/auth/saml/acs [post]
func (*HandlersApi) SAMLLoginHandler ¶ added in v0.5.3
func (h *HandlersApi) SAMLLoginHandler(w http.ResponseWriter, r *http.Request)
SAMLLoginHandler — GET /api/v1/auth/saml/login.
Starts the SAML SP-initiated SSO flow:
- Mint a fresh 256-bit nonce + OAuthState (defense-in-depth split, same shape as the OIDC handler).
- Issue the state JWT cookie (Path=/api/v1/auth/, HttpOnly, Secure, SameSite=Lax, 10-minute TTL).
- Build the IdP SSO URL with RelayState=<OAuthState>. The IdP echoes RelayState verbatim on the ACS POST; SAMLACSHandler validates the echo against the state cookie.
- 302-redirect the browser to the IdP.
Unauthenticated by design: the user is logging IN. The state cookie is the only thing tying the eventual ACS POST back to this request.
EnvUUID on the State is a fixed sentinel ("api") because osctrl-api is single-tenant for federated login. Matches the OIDC handler's posture. @Summary Start SAML login @Description Redirects the browser to the configured SAML identity provider. @Tags auth @Produce json @Success 200 {string} string @Failure 302 {string} string @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Router /api/v1/auth/saml/login [get]
func (*HandlersApi) SAMLMetadataHandler ¶ added in v0.5.3
func (h *HandlersApi) SAMLMetadataHandler(w http.ResponseWriter, r *http.Request)
SAMLMetadataHandler — GET /api/v1/auth/saml/metadata.
Serves the SP metadata XML for IdP-side registration. The IdP administrator points their config at this URL (or downloads the XML and uploads it) so the IdP knows our EntityID + ACS URL + supported NameID formats.
Public by design — SP metadata is meant to be machine-readable by anyone who wants to federate with us. The information it carries is the same information the IdP would learn via the AuthnRequest URL during the first login attempt.
Rate-limited at the route layer like the other unauth endpoints. @Summary Get SAML metadata @Description Returns service provider metadata for SAML identity provider registration. @Tags auth @Produce application/xml @Success 200 {string} string @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Router /api/v1/auth/saml/metadata [get]
func (*HandlersApi) SavedQueriesListHandler ¶ added in v0.5.2
func (h *HandlersApi) SavedQueriesListHandler(w http.ResponseWriter, r *http.Request)
SavedQueriesListHandler - GET /api/v1/saved-queries/{env}
Paginated, sorted, searchable list of saved queries for an environment. Query params: page, page_size, q (free-text), sort (column key), dir (asc|desc). @Summary List saved queries @Description Returns paginated saved queries for an environment. @Tags saved-queries @Produce json @Param env path string true "Environment name or UUID" @Param page query int false "Page number" @Param page_size query int false "Page size" @Param q query string false "Search query" @Success 200 {object} types.SavedQueriesPagedResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/saved-queries/{env} [get]
func (*HandlersApi) SavedQueryCreateHandler ¶ added in v0.5.2
func (h *HandlersApi) SavedQueryCreateHandler(w http.ResponseWriter, r *http.Request)
SavedQueryCreateHandler - POST /api/v1/saved-queries/{env}
Body: { "name": string, "query": string }. Returns 201 with the created view, 409 if a saved query with that name already exists in the environment. @Summary Create saved query @Description Creates a saved query in an environment. @Tags saved-queries @Accept json @Produce json @Param env path string true "Environment name or UUID" @Param request body types.SavedQueryCreateRequest true "Request body" @Success 200 {object} types.SavedQueryView @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/saved-queries/{env} [post]
func (*HandlersApi) SavedQueryDeleteHandler ¶ added in v0.5.2
func (h *HandlersApi) SavedQueryDeleteHandler(w http.ResponseWriter, r *http.Request)
SavedQueryDeleteHandler - DELETE /api/v1/saved-queries/{env}/{name} @Summary Delete saved query @Description Deletes a saved query in an environment. @Tags saved-queries @Produce json @Param env path string true "Environment name or UUID" @Param name path string true "Saved query name" @Success 200 {object} types.ApiGenericResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/saved-queries/{env}/{name} [delete]
func (*HandlersApi) SavedQueryUpdateHandler ¶ added in v0.5.2
func (h *HandlersApi) SavedQueryUpdateHandler(w http.ResponseWriter, r *http.Request)
SavedQueryUpdateHandler - PATCH /api/v1/saved-queries/{env}/{name}
Body: { "query": string }. Updates the SQL body only; the original creator is preserved. Returns the updated view. @Summary Update saved query @Description Updates a saved query in an environment. @Tags saved-queries @Accept json @Produce json @Param env path string true "Environment name or UUID" @Param name path string true "Saved query name" @Param request body types.SavedQueryUpdateRequest true "Request body" @Success 200 {object} types.SavedQueryView @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/saved-queries/{env}/{name} [patch]
func (*HandlersApi) ServiceCommandHandler ¶ added in v0.5.6
func (h *HandlersApi) ServiceCommandHandler(w http.ResponseWriter, r *http.Request)
ServiceCommandHandler — GET /api/v1/service-config/commands/{command_id}
func (*HandlersApi) ServiceConfigApplyHandler ¶ added in v0.5.6
func (h *HandlersApi) ServiceConfigApplyHandler(w http.ResponseWriter, r *http.Request)
ServiceConfigApplyHandler — POST /api/v1/service-config/apply
Triggers a graceful shutdown of the API service so the process manager (systemd, docker, k8s) restarts it. On restart, Resolve applies all DB-edited config sections (source=db) over the YAML defaults, making the operator's changes live.
The handler returns 202 immediately; the shutdown happens asynchronously after a short delay so the HTTP response completes. If RestartCh is nil (not wired), returns 503.
@Summary Apply config changes and restart @Description Triggers a graceful service restart to apply DB-edited config changes. @Tags service-config @Produce json @Success 202 {object} types.ApiErrorResponse "Restart triggered" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/service-config/apply [post]
func (*HandlersApi) ServiceConfigHandler ¶ added in v0.5.6
func (h *HandlersApi) ServiceConfigHandler(w http.ResponseWriter, r *http.Request)
ServiceConfigHandler - GET Handler for all service config sections @Summary List all service config sections @Description Returns all service configuration sections across all services. @Tags service-config @Produce json @Success 200 {array} serviceconfig.ServiceConfig @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Security ApiKeyAuth @Router /api/v1/service-config [get]
func (*HandlersApi) ServiceConfigPersistHandler ¶ added in v0.5.6
func (h *HandlersApi) ServiceConfigPersistHandler(w http.ResponseWriter, r *http.Request)
ServiceConfigPersistHandler — POST /api/v1/service-config/persist
Writes the DB-edited config sections back to the service's YAML file so the changes survive a redeploy that starts from the file. It does not restart anything: applying the changes to the running process remains the separate apply endpoint.
osctrl-api writes its own file inline. osctrl-tls cannot be written from here — it is a different process with a different config volume — so the write is queued as a service command that the TLS process consumes on its next poll, exactly like a restart request.
@Summary Write config changes to disk @Description Persists DB-edited config sections back to the service's YAML file. @Tags service-config @Accept json @Produce json @Param request body types.ServiceConfigPersistRequest false "Request body" @Success 200 {object} types.ServiceConfigPersistResponse "Written" @Success 202 {object} types.ServiceConfigPersistResponse "Write queued" @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 409 {object} types.ApiErrorResponse "Config file not writable" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/service-config/persist [post]
func (*HandlersApi) ServiceConfigSectionHandler ¶ added in v0.5.6
func (h *HandlersApi) ServiceConfigSectionHandler(w http.ResponseWriter, r *http.Request)
ServiceConfigSectionHandler - GET Handler for a single config section @Summary Get one service config section @Description Returns a single configuration section by service and section name. @Tags service-config @Produce json @Param service path string true "Service name" @Param section path string true "Section name" @Success 200 {object} serviceconfig.ServiceConfig @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Security ApiKeyAuth @Router /api/v1/service-config/{service}/{section} [get]
func (*HandlersApi) ServiceConfigServiceHandler ¶ added in v0.5.6
func (h *HandlersApi) ServiceConfigServiceHandler(w http.ResponseWriter, r *http.Request)
ServiceConfigServiceHandler - GET Handler for all sections of one service @Summary List service config sections for a service @Description Returns all configuration sections for the given service. @Tags service-config @Produce json @Param service path string true "Service name" @Success 200 {array} serviceconfig.ServiceConfig @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Security ApiKeyAuth @Router /api/v1/service-config/{service} [get]
func (*HandlersApi) ServiceConfigStatusHandler ¶ added in v0.5.6
func (h *HandlersApi) ServiceConfigStatusHandler(w http.ResponseWriter, r *http.Request)
ServiceConfigStatusHandler — GET /api/v1/service-config/status/{service}
Reports whether the service has config sections edited in the database that the YAML file on disk does not have, and whether the service's own process can write that file. The SPA uses this to decide whether to offer the "Write to disk" action and whether it has to be disabled.
The writability answer comes from the row the service itself reported at boot: osctrl-tls runs in its own process (usually its own container), so this process cannot check the TLS config file directly.
@Summary Service config file status @Description Reports unsaved DB changes and config file writability for a service. @Tags service-config @Produce json @Param service path string true "Service name" @Success 200 {object} types.ServiceConfigStatusResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Security ApiKeyAuth @Router /api/v1/service-config/status/{service} [get]
func (*HandlersApi) ServiceConfigUpdateHandler ¶ added in v0.5.6
func (h *HandlersApi) ServiceConfigUpdateHandler(w http.ResponseWriter, r *http.Request)
ServiceConfigUpdateHandler — PUT /api/v1/service-config/{service}/{section}
Replaces the JSON value of an editable service config section. Only sections marked Editable=true in the SectionRegistry can be updated. The source is flipped to "db" so subsequent boots won't clobber the change. The body must be a JSON object with a "value" field containing the new JSON-encoded section value.
Body shape:
{ "value": {"enableHttp": true, "showBody": false} }
@Summary Update service config section @Description Replaces an editable service config section's JSON value. @Tags service-config @Accept json @Produce json @Param service path string true "Service name" @Param section path string true "Section name" @Param request body types.ServiceConfigUpdateRequest true "Request body" @Success 200 {object} serviceconfig.ServiceConfig @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/service-config/{service}/{section} [put]
func (*HandlersApi) SetUserPermissionsAllHandler ¶ added in v0.5.3
func (h *HandlersApi) SetUserPermissionsAllHandler(w http.ResponseWriter, r *http.Request)
SetUserPermissionsAllHandler - POST /api/v1/users/{username}/permissions/all
Bulk variant of SetUserPermissionsHandler: applies the same EnvAccess shape to every environment that exists at request time. Body: { access: { user, query, carve, admin } }. Returns { updated, total, access } on success.
Same authn/authz posture as the per-env handler: requires super-admin (AdminLevel, NoEnvironment). Same lockout guards:
- Super-admins cannot self-demote via this endpoint.
- The last super-admin cannot be demoted under any path.
"All current envs" semantics: enumeration happens server-side at request time. Envs created LATER do not inherit; the operator re-applies as needed. See SetPermissionsAllRequest godoc. @Summary Set all user permissions @Description Sets permissions across all environments for a user. @Tags users @Accept json @Produce json @Param username path string true "Username" @Param request body types.SetPermissionsAllRequest true "Request body" @Success 200 {object} types.SetPermissionsAllResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/users/{username}/permissions/all [post]
func (*HandlersApi) SetUserPermissionsHandler ¶ added in v0.5.2
func (h *HandlersApi) SetUserPermissionsHandler(w http.ResponseWriter, r *http.Request)
SetUserPermissionsHandler - POST /api/v1/users/{username}/permissions
Body: { env_uuid, access: { user, query, carve, admin } }. Replaces the target user's per-env access rows. Returns 200 with the new EnvAccess. Requires super-admin (AdminLevel, NoEnvironment) — env-scoped admins can not grant permissions for their environment from this endpoint. @Summary Set user permissions @Description Sets per-environment permissions for a user. @Tags users @Accept json @Produce json @Param username path string true "Username" @Param request body types.SetPermissionsRequest true "Request body" @Success 200 {object} types.EnvAccessView @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/users/{username}/permissions [post]
func (*HandlersApi) SettingPatchHandler ¶ added in v0.5.2
func (h *HandlersApi) SettingPatchHandler(w http.ResponseWriter, r *http.Request)
SettingPatchHandler — PATCH /api/v1/settings/{service}/{name}
Body shape (one of String, Boolean, Integer):
{ "string": "value" }
{ "boolean": true }
{ "integer": 42 }
The handler reads the existing setting first to determine its type, then applies the matching typed setter. Mismatched payloads return 400. The setting must already exist (creation is the legacy admin's job); a missing setting → 404. Audit-log on success only. @Summary Update setting @Description Updates a mutable setting value. @Tags settings @Accept json @Produce json @Param service path string true "Service name" @Param name path string true "Setting name" @Param request body types.SettingPatchRequest true "Request body" @Success 200 {object} settings.SettingValue @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/settings/{service}/{name} [patch]
func (*HandlersApi) SettingsHandler ¶
func (h *HandlersApi) SettingsHandler(w http.ResponseWriter, r *http.Request)
SettingsHandler - GET Handler for all settings including JSON @Summary List settings @Description Returns settings for all services. @Tags settings @Produce json @Success 200 {array} settings.SettingValue @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/settings [get]
func (*HandlersApi) SettingsServiceEnvHandler ¶
func (h *HandlersApi) SettingsServiceEnvHandler(w http.ResponseWriter, r *http.Request)
SettingsServiceEnvHandler - GET Handler for service and environment specific settings excluding JSON @Summary List service environment settings @Description Returns settings for a service and environment. @Tags settings @Produce json @Param service path string true "Service name" @Param env path string true "Environment name or UUID" @Success 200 {array} settings.SettingValue @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/settings/{service}/{env} [get]
func (*HandlersApi) SettingsServiceHandler ¶
func (h *HandlersApi) SettingsServiceHandler(w http.ResponseWriter, r *http.Request)
SettingsServiceHandler - GET Handler for service specific settings excluding JSON @Summary List service settings @Description Returns settings for a service. @Tags settings @Produce json @Param service path string true "Service name" @Success 200 {array} settings.SettingValue @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/settings/{service} [get]
func (*HandlersApi) StatsHandler ¶ added in v0.5.2
func (h *HandlersApi) StatsHandler(w http.ResponseWriter, r *http.Request)
StatsHandler returns cross-env totals + per-env counts, filtered to the envs the calling user has UserLevel access to. Used by the SPA dashboard.
No query params. The response is small (one entry per accessible env) and cacheable for 30s on the client (Cache-Control: private, max-age=30).
NOTE on query/carve counting:
- GetActive(envID) returns ALL active rows regardless of type (union).
- To avoid double-counting we call GetQueries("active", envID) for standard queries and GetCarves("active", envID) for carves separately.
- Unit test for this handler is deferred: the underlying pkg/queries functions are exercised by existing tests in pkg/queries; a full integration test would require DB fixture setup that is out of scope for Track 2.
@Summary Get dashboard stats @Description Returns cross-environment dashboard statistics. @Tags stats @Produce json @Success 200 {object} StatsResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/stats [get]
func (*HandlersApi) TagEnvHandler ¶
func (h *HandlersApi) TagEnvHandler(w http.ResponseWriter, r *http.Request)
TagEnvHandler - GET Handler to return one tag for one environment as JSON. Permission is scoped to env.UUID admin so non-super operators with admin rights on this specific environment can view its tags. @Summary Get environment tag @Description Returns one tag by name for an environment. @Tags tags @Produce json @Param env path string true "Environment name or UUID" @Param name path string true "Tag name" @Success 200 {object} tags.AdminTag @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/tags/{env}/{name} [get]
func (*HandlersApi) TagNodeHandler ¶ added in v0.4.6
func (h *HandlersApi) TagNodeHandler(w http.ResponseWriter, r *http.Request)
TagNodeHandler - POST Handler to tag a node @Summary Tag node @Description Adds or updates a tag on a node. @Tags nodes @Accept json @Produce json @Param env path string true "Environment name or UUID" @Param request body types.ApiNodeTagRequest true "Request body" @Success 200 {object} types.ApiGenericResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/nodes/{env}/tag [post]
func (*HandlersApi) TagsActionHandler ¶
func (h *HandlersApi) TagsActionHandler(w http.ResponseWriter, r *http.Request)
TagsActionHandler - POST Handler to create / update / delete tags. The action arrives as a URL path segment (legacy contract retained because Track 6 doesn't introduce new tag routes); body validation surfaces 400 on parse error and 409 on duplicate-name conflicts. @Summary Execute tag action @Description Creates, updates, deletes, or applies tags in an environment. @Tags tags @Accept json @Produce json @Param env path string true "Environment name or UUID" @Param action path string true "Tag action" @Param request body types.ApiTagsRequest true "Request body" @Success 200 {object} types.ApiDataResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/tags/{env}/{action} [post]
func (*HandlersApi) TagsEnvHandler ¶
func (h *HandlersApi) TagsEnvHandler(w http.ResponseWriter, r *http.Request)
TagsEnvHandler - GET Handler to return tags for one environment as JSON. Permission is scoped to env.UUID admin (see TagEnvHandler note). @Summary List environment tags @Description Returns tags for an environment. @Tags tags @Produce json @Param env path string true "Environment name or UUID" @Success 200 {array} tags.AdminTag @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/tags/{env} [get]
func (*HandlersApi) UserActionHandler ¶ added in v0.4.6
func (h *HandlersApi) UserActionHandler(w http.ResponseWriter, r *http.Request)
UserActionHandler - POST Handler to take actions on a user by username and environment @Summary Execute user action @Description Creates, updates, deletes, or changes flags on a user. @Tags users @Accept json @Produce json @Param username path string true "Username" @Param action path string true "User action" @Param request body types.ApiUserRequest true "Request body" @Success 200 {object} types.ApiDataResponse @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/users/{username}/{action} [post]
func (*HandlersApi) UserHandler ¶
func (h *HandlersApi) UserHandler(w http.ResponseWriter, r *http.Request)
UserHandler - GET Handler for environment users @Summary Get user @Description Returns an API user by username. @Tags users @Produce json @Param username path string true "Username" @Success 200 {object} types.AdminUserView @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/users/{username} [get]
func (*HandlersApi) UsersHandler ¶
func (h *HandlersApi) UsersHandler(w http.ResponseWriter, r *http.Request)
UsersHandler - GET Handler for multiple JSON nodes @Summary List users @Description Returns API users. @Tags users @Produce json @Success 200 {array} types.AdminUserView @Failure 400 {object} types.ApiErrorResponse "Bad request" @Failure 401 {object} types.ApiErrorResponse "Unauthorized" @Failure 403 {object} types.ApiErrorResponse "Forbidden" @Failure 404 {object} types.ApiErrorResponse "Not found" @Failure 409 {object} types.ApiErrorResponse "Conflict" @Failure 429 {object} types.ApiErrorResponse "Too many requests" @Failure 500 {object} types.ApiErrorResponse "Internal server error" @Failure 503 {object} types.ApiErrorResponse "Service unavailable" @Security ApiKeyAuth @Router /api/v1/users [get]
type HandlersOption ¶
type HandlersOption func(*HandlersApi)
func WithActivityReader ¶ added in v0.5.4
func WithActivityReader(ar activityReader) HandlersOption
func WithAuditLog ¶ added in v0.4.8
func WithAuditLog(auditLog *auditlog.AuditLogManager) HandlersOption
func WithAuthProviders ¶ added in v0.5.7
func WithAuthProviders(reg *AuthProviderRegistry, mgr *authproviders.AuthProviderManager) HandlersOption
WithAuthProviders wires the auth provider registry and manager.
func WithAuthProvidersEnabled ¶ added in v0.5.7
func WithAuthProvidersEnabled(enabled bool) HandlersOption
func WithCarves ¶
func WithCarves(carves *carves.Carves) HandlersOption
func WithConfigPersist ¶ added in v0.5.6
func WithConfigPersist(fn func() error) HandlersOption
func WithConsole ¶ added in v0.5.5
func WithConsole(consoleManager *console.Manager) HandlersOption
func WithDB ¶
func WithDB(db *gorm.DB) HandlersOption
func WithDBHealth ¶ added in v0.5.5
func WithDBHealth(hl backend.DegradedReader) HandlersOption
WithDBHealth wires a DB health monitor so HealthHandler can report backend degradation (HTTP 204) instead of always reporting 200. Pass nil to keep the legacy "always healthy" behavior.
func WithDebugHTTP ¶ added in v0.4.5
func WithDebugHTTP(cfg *config.YAMLConfigurationDebug) HandlersOption
func WithEnvCache ¶ added in v0.5.5
func WithEnvCache(envCache *environments.EnvCache) HandlersOption
func WithEnvs ¶
func WithEnvs(envs *environments.EnvManager) HandlersOption
func WithFileExplorer ¶ added in v0.5.5
func WithFileExplorer(fileExplorerManager *fileexplorer.Manager) HandlersOption
func WithGeoIP ¶ added in v0.5.5
func WithGeoIP(g *geoip.GeoIPResolver) HandlersOption
func WithJWTSecret ¶ added in v0.5.3
func WithJWTSecret(secret []byte) HandlersOption
WithJWTSecret attaches the HMAC key for auth state-cookie signing. MUST be the same secret pkg/users uses for user JWTs; the audience claim ("osctrl-auth-state" vs "osctrl-api") segregates the two purposes so sharing the underlying secret is safe.
func WithLogReader ¶ added in v0.5.6
func WithLogReader(r logging.LogReader) HandlersOption
WithLogReader wires a LogReader (DB- or S3-backed). When unset, the handlers fall back to NewDBLogReader(h.DB) so tests that only wire WithDB behave as before.
func WithLogSinks ¶ added in v0.5.7
func WithLogSinks(mgr *logsinks.LogSinksManager) HandlersOption
WithLogSinks wires the log_sinks manager. Only meaningful when ServiceConfigEnabled is also true; the routes are gated on that flag in cmd/api/main.go.
func WithLogSinksEnabled ¶ added in v0.5.7
func WithLogSinksEnabled(enabled bool) HandlersOption
func WithMFA ¶ added in v0.5.6
func WithMFA(manager *mfa.Manager, webAuthn *mfa.WebAuthn, required bool, issuer string) HandlersOption
WithMFA wires the second-factor manager, the (optional) WebAuthn relying party and the deployment-wide requirement switch.
func WithName ¶
func WithName(name string) HandlersOption
func WithNodes ¶
func WithNodes(nodes *nodes.NodeManager) HandlersOption
func WithOIDC ¶ added in v0.5.3
func WithOIDC(enabled bool) HandlersOption
WithOIDC toggles the global OIDC routes and the /api/v1/auth/methods response. Pass true at startup when the operator has configured an OIDC provider. When false, /api/v1/auth/methods returns password-only so the SPA renders the password form alone.
func WithOsqueryTables ¶ added in v0.5.2
func WithOsqueryTables(tables []types.OsqueryTable) HandlersOption
func WithOsqueryValues ¶ added in v0.5.2
func WithOsqueryValues(values config.YAMLConfigurationOsquery) HandlersOption
func WithPosture ¶ added in v0.5.5
func WithPosture(pm *posture.PostureManager) HandlersOption
func WithPostureEnabled ¶ added in v0.5.5
func WithPostureEnabled(enabled bool) HandlersOption
func WithQueries ¶
func WithQueries(queries *queries.Queries) HandlersOption
func WithRestartCh ¶ added in v0.5.6
func WithRestartCh(ch chan<- struct{}) HandlersOption
func WithSAML ¶ added in v0.5.3
func WithSAML(enabled bool) HandlersOption
WithSAML toggles the global SAML routes and the /api/v1/auth/methods response. SAML analogue of WithOIDC; the two can be enabled simultaneously.
func WithServiceCommands ¶ added in v0.5.6
func WithServiceCommands(mgr *servicecommands.Manager) HandlersOption
func WithServiceConfig ¶ added in v0.5.6
func WithServiceConfig(mgr *serviceconfig.ServiceConfigManager) HandlersOption
func WithServiceConfigEnabled ¶ added in v0.5.6
func WithServiceConfigEnabled(enabled bool) HandlersOption
func WithSettings ¶
func WithSettings(settings *settings.Settings) HandlersOption
func WithTags ¶
func WithTags(tags *tags.TagManager) HandlersOption
func WithUsers ¶
func WithUsers(users *users.UserManager) HandlersOption
func WithVersion ¶
func WithVersion(version string) HandlersOption
type LogoutResponse ¶ added in v0.5.3
type LogoutResponse struct {
AuthSource string `json:"auth_source,omitempty"`
IdPLogoutURL string `json:"idp_logout_url,omitempty"`
IdPClientID string `json:"idp_client_id,omitempty"`
IdPIDTokenHint string `json:"idp_id_token_hint,omitempty"`
}
LogoutResponse is the JSON payload returned by POST /api/v1/logout.
AuthSource carries which provider issued the active session ("oidc" / "saml" / ""). The SPA uses it to decide which IdP-logout flow to run — sending a SAML user to the OIDC end-session URL with a stale id_token_hint just produces a confusing Keycloak error page.
IdPLogoutURL is non-empty when AuthSource=="oidc" AND the OIDC provider advertised an end_session_endpoint in its discovery document — the SPA navigates the browser there to terminate the IdP session. Empty for SAML users (SLO deferred to v2 per spec) and for password users.
IdPClientID is the OIDC client_id registered with the IdP. Some IdPs (Keycloak) accept it as an alternative to id_token_hint when the operator chains post_logout_redirect_uri. The SPA appends it as ?client_id=...
IdPIDTokenHint is the raw id_token from the user's most recent federated login. Okta REQUIRES this on /v1/logout when chaining a post-logout redirect; without it Okta returns "Missing parameter: id_token_hint". Non-empty when the osctrl_id_token cookie was present and valid; empty for password-only operators or after a fresh login from a cookie-stripped browser. The SPA appends it as ?id_token_hint=... — the IdP verifies the token's signature before terminating the session, so this is safe to expose to the browser (we set it back into the URL the browser navigates to).
type MFAChallengeResponse ¶ added in v0.5.6
type MFAChallengeResponse struct {
// MFARequired is true when the user has factors to present.
MFARequired bool `json:"mfa_required"`
// MFAEnrollmentRequired is true when the deployment requires MFA and
// this user has none yet, so the second step is enrollment.
MFAEnrollmentRequired bool `json:"mfa_enrollment_required"`
Challenge string `json:"challenge"`
// Methods lists what this user can answer with: "totp", "webauthn",
// "recovery".
Methods []string `json:"methods"`
}
MFAChallengeResponse is what the first login step returns when a second factor is still owed. There is deliberately no token in it: the challenge id only names a half-finished login, and is worthless without the factor.
type MFACodeRequest ¶ added in v0.5.6
type MFACodeRequest struct {
Code string `json:"code"`
}
MFACodeRequest confirms an enrollment with a code from the app.
type MFACredential ¶ added in v0.5.6
type MFACredential struct {
ID string `json:"id"`
Name string `json:"name"`
CreatedAt string `json:"created_at"`
LastUsedAt string `json:"last_used_at,omitempty"`
}
MFACredential is the API projection of a registered authenticator. The public key and AAGUID stay server-side; the SPA only needs to name it and address it for deletion.
type MFAEnrollBeginResponse ¶ added in v0.5.6
type MFAEnrollBeginResponse struct {
Secret string `json:"secret"`
URI string `json:"uri"`
// QR is the provisioning URI as a PNG data URI, ready for an <img>.
QR string `json:"qr,omitempty"`
}
MFAEnrollBeginResponse hands back a secret to load into an authenticator.
type MFAEnrollRequest ¶ added in v0.5.6
MFAEnrollRequest completes a forced enrollment at login time.
type MFALoginRequest ¶ added in v0.5.6
type MFALoginRequest struct {
Challenge string `json:"challenge"`
// Method is "totp" or "recovery".
Method string `json:"method"`
Code string `json:"code"`
}
MFALoginRequest answers a login challenge with a code.
type MFAPasswordRequest ¶ added in v0.5.6
type MFAPasswordRequest struct {
Password string `json:"password"`
}
MFAPasswordRequest re-authenticates the caller before a change that weakens their account. A hijacked session should not be able to strip a factor.
type MFARecoveryCodesResponse ¶ added in v0.5.6
type MFARecoveryCodesResponse struct {
RecoveryCodes []string `json:"recovery_codes"`
}
MFARecoveryCodesResponse returns freshly minted recovery codes. This is the only time they exist in plaintext.
type MFAStatusResponse ¶ added in v0.5.6
type MFAStatusResponse struct {
// Required mirrors the deployment-wide switch so the profile page can
// explain why a factor cannot be removed.
Required bool `json:"required"`
WebAuthnAvailable bool `json:"webauthn_available"`
TOTPEnabled bool `json:"totp_enabled"`
RecoveryCodesLeft int `json:"recovery_codes_left"`
Credentials []MFACredential `json:"credentials"`
}
MFAStatusResponse describes the factors on the caller's own account.
type MFATOTPBeginResponse ¶ added in v0.5.6
type MFATOTPBeginResponse struct {
Secret string `json:"secret"`
URI string `json:"uri"`
// QR is the provisioning URI as a PNG data URI, ready for an <img>.
QR string `json:"qr,omitempty"`
}
MFATOTPBeginResponse carries the secret to load into an authenticator app.
type MFAWebAuthnRequest ¶ added in v0.5.6
type MFAWebAuthnRequest struct {
Challenge string `json:"challenge"`
Name string `json:"name"`
Credential json.RawMessage `json:"credential"`
}
MFAWebAuthnRequest carries a WebAuthn ceremony payload. Credential is the raw PublicKeyCredential JSON the browser produced, forwarded verbatim.
type NodeActivityBucket ¶ added in v0.5.2
type NodeActivityBucket struct {
BucketStart time.Time `json:"bucket_start"`
Status int `json:"status"`
Result int `json:"result"`
Query int `json:"query"`
Carve int `json:"carve"`
Config int `json:"config"`
}
NodeActivityBucket is one cell of the per-node 24h activity heatmap. Categories pivot from the env-scoped variant — node-scoped activity is about what THIS device has been doing, not what operators have done to the env. So:
- status ← osquery_status_data row count (status logs received from this node)
- result ← osquery_result_data row count (query results returned by this node)
- query ← node_queries row count (distributed queries scheduled against this node)
- carve ← carved_files row count (carves this node has produced)
- config ← Redis-backed hourly config-fetch count (osquery agent heartbeats)
The first four are DB-backed; config comes from the Redis activity rollup store and is folded in by computeNodeActivityForNode when a NodeTileSeries is provided. All five are joinable by node uuid (or numeric node id for node_queries).
type NodeLogsResponse ¶ added in v0.5.2
type NodeLogsResponse struct {
Items []map[string]any `json:"items"`
Type string `json:"type"`
UUID string `json:"uuid"`
Env string `json:"env"`
Since string `json:"since,omitempty"`
Limit int `json:"limit"`
}
NodeLogsResponse is the SPA-canonical response for GET /api/v1/logs/{type}/{env}/{uuid}.
type StatsResponse ¶ added in v0.5.2
type StatsResponse struct {
// Cross-env totals (the user's allowed envs only).
TotalNodes int64 `json:"total_nodes"`
ActiveNodes int64 `json:"active_nodes"`
InactiveNodes int64 `json:"inactive_nodes"`
InactiveHours int64 `json:"inactive_hours"`
// TotalActiveQueries counts standard query-type active queries (excludes carves).
TotalActiveQueries int `json:"total_active_queries"`
// TotalActiveCarves counts active carve-type queries.
TotalActiveCarves int `json:"total_active_carves"`
// Cross-env platform breakdown — sum of every accessible env's PlatformCounts.
PlatformCounts nodes.PlatformCounts `json:"platform_counts"`
// Per-env breakdown, in stable alphabetical order by name.
Environments []EnvStats `json:"environments"`
}
StatsResponse is the canonical /api/v1/stats shape consumed by the dashboard.
Source Files
¶
- audit.go
- auth_jwt.go
- auth_logout.go
- auth_methods.go
- auth_oidc.go
- auth_provider_registry.go
- auth_providers.go
- auth_resolve.go
- auth_saml.go
- carves.go
- console.go
- env_cache.go
- environments.go
- environments_crud.go
- environments_packages.go
- features.go
- file_explorer.go
- get.go
- handlers.go
- log_sinks.go
- login.go
- login_envs.go
- logs.go
- mfa.go
- mfa_login.go
- nodes.go
- platforms.go
- queries.go
- samples.go
- saved_queries.go
- service_config.go
- settings.go
- settings_patch.go
- stats.go
- tags.go
- users.go
- users_profile.go
- utils.go