Technical Architecture

Audience: IT admins and technical staff. This page describes the technical architecture of Keeper Privilege Manager: how the system is event-driven, how MQTT and HTTPS are used, how plugins are loaded and monitored, and how jobs are launched and executed.


Overview

Keeper Privilege Manager is a central orchestration service that runs on each endpoint. It does not perform policy evaluation or logging by itself; instead it:

  • Hosts an MQTT broker and an HTTP/HTTPS API so that plugins and jobs can communicate.

  • Loads and monitors plugins (separate processes such as KeeperPolicy, KeeperAPI, KeeperLogger) and can restart them if they fail.

  • Runs the job service, which executes jobs when events fire (via MQTT), when schedules match, or when triggered manually via the API.

The system is event-driven: most workflows start when something publishes a message to the MQTT broker (e.g. “policy evaluation needed,” “launch elevation”). Subscribers (plugins or the job service) react to those messages and either handle the request or trigger a job that runs a sequence of tasks.


High-Level Architecture

  • Plugins connect to the MQTT broker to receive and send messages. They may also call the HTTP API (e.g. to get settings, trigger a job, or launch a process).

  • Jobs are triggered by events (MQTT), by schedule, or by API. When a job runs, it executes a list of tasks (run an executable, call HTTP, etc.). Task output can be merged into context so later tasks or routing decisions use it.


Event-Driven Model

The system is event-driven: actions are triggered by events rather than by a single sequential program.

How events flow

  1. Something happens on the endpoint (e.g. user requests elevation, a hook or agent detects it, or the service starts up).

  2. A message is published to the MQTT broker on a specific topic (e.g. a policy evaluation request to the topic that KeeperPolicy subscribes to, or a custom event such as PolicyEvaluationPending or LaunchPrivilegeElevation to the topic the job service listens on).

  3. Subscribers receive the message:

    • Plugins (e.g. KeeperPolicy) subscribe to topics and process requests (e.g. evaluate policy, return ALLOW/DENY/PENDING).

    • The job service subscribes (or is notified) when a custom event is published; it finds all jobs that list that event and launches each matching job with the event context.

  4. Jobs run tasks in sequence. Tasks can publish more MQTT messages (e.g. send response back to a topic), call the HTTP API (e.g. launch elevated, get settings), or run executables (e.g. KeeperMFA, KeeperApproval). That can trigger further events (e.g. after controls complete, publish LaunchPrivilegeElevation so the launch job runs).

So one event leads to a job run; that job can publish another event, which leads to another job, and so on. Policy evaluation, controls (MFA, approval, justification), and launch flows are all wired this way.

Typical event names (custom events)

  • PolicyEvaluationPending — A policy evaluation returned PENDING (controls required). Policy control jobs (e.g. privilege-elevation-policy-controls, file-access-policy-controls, default-policy-controls) subscribe to this and run MFA/justification/approval workflows.

  • LaunchPrivilegeElevation — The user is allowed to elevate; the job that actually launches the process (LaunchPrivilegeElevation) is triggered with context (file path, command line, user, etc.).

  • Startup — Fired when the service starts. Jobs such as registration or cleanup subscribe to this.

  • LaunchApprovedRequest, ShowAgent, and others — Used by the UI or workflow to trigger specific jobs.

Events can carry context (parameters) that is passed into the job as its initial context so tasks can use it (e.g. {RequestId}, {FilePath}, {UserName}).


How MQTT is Used

Role of the MQTT Broker

The main service runs an embedded MQTT broker (default port 8675, localhost only). It is the message bus for the product:

  • Plugins connect to the broker as MQTT clients. Each plugin has a subscription configuration (topic(s) to subscribe to) and publish permissions (topic(s) it can publish to).

  • Process-based security: Only processes that were launched by KeeperPrivilegeManager (and that present a valid certificate) are allowed to connect. This prevents arbitrary applications from publishing or subscribing.

  • Topic-based routing: Messages are delivered to every client that is subscribed to that topic. So when someone publishes to EventMessages (or the topic used for custom events), the job service receives it and can trigger jobs; when someone publishes to KeeperPolicy, the KeeperPolicy plugin receives it and evaluates policy.

Typical Topics (conceptual)

  • KeeperPolicy — Policy evaluation requests (in); policy responses (out).

  • KeeperLogger — Log messages (plugins and jobs publish here so logs are centralized).

  • KeeperApi — Backend sync, registration, audit (KeeperAPI plugin).

  • EventMessages (or equivalent) — Custom events that trigger jobs (e.g. PolicyEvaluationPending, LaunchPrivilegeElevation). The job service listens and matches event name to job definitions.

  • JobService — Job-related messages (e.g. job completion, trigger).

  • keeperAgent, KeeperClient, RequestApproval, etc. — Used by UI and approval workflows.

Exact topic names and which plugin publishes/subscribes to what are defined in each plugin’s JSON (Subscription, metadata.mqttTopics). The important point for architecture is: all inter-component communication for events and policy flows over MQTT on localhost, with process and topic checks.

MQTT and Event-Driven Jobs

When a custom event must trigger a job:

  1. Some component (e.g. KeeperPolicy or another job) publishes a message to the event topic, with event name and payload (context).

  2. The job service receives the message and calls TriggerJobByEvent(eventName, context) (or equivalent).

  3. Every job whose events array contains that event name (e.g. customEvent: "LaunchPrivilegeElevation") is matched and started with that context.

  4. Each such job runs its task list; tasks can in turn publish to MQTT or call the HTTP API, producing further events or side effects.

So MQTT is used both for request/response (e.g. policy request → KeeperPolicy → response) and for fire-and-forget event delivery to the job service to launch jobs.


How HTTPS (and HTTP) is Used

HTTP/HTTPS API Server

The main service exposes an HTTP/HTTPS API (Kestrel) bound to localhost only (127.0.0.1):

  • HTTP default port 6888

  • HTTPS default port 6889 (recommended; uses a self-signed certificate for localhost)

The API is used for:

  • Health and statusGET /health, GET /, GET /api/system/status (no auth; monitoring and scripts).

  • Plugin management — Start/stop/restart plugins, list plugins (Admin or Plugin auth depending on endpoint).

  • Job management — List jobs, run a job, trigger a job with context, validate job JSON (Plugin or Admin).

  • Settings — Read/update application and plugin settings; revert plugin settings from JSON (Admin for write).

  • Registration — Register or unregister the agent with the Keeper backend (Admin or as configured).

  • Other operations — File access, user session launch, ephemeral account, audit, etc., as documented in Local management endpointsarrow-up-right.

Who Calls the API

  • Plugins — To load their settings (GET /api/PluginSettings/{pluginName}), to trigger jobs (POST /api/Jobs/{id}/trigger), or to call launch/ephemeral endpoints. They use the HTTPS base URL (e.g. from config or parameter).

  • Job tasksHTTP tasks inside a job call arbitrary URLs (often the local API) to trigger jobs, launch processes, create execution grants, or revert settings. Service tasks that run executables (e.g. KeeperMFA, RedirectEvaluator) may receive the API base URL as an argument so the executable can call back.

  • Admins / scripts — Management scripts or operators call the API (with Admin privileges or client certificate as required) to check health, restart plugins, run jobs, or change settings.

Authorization levels (conceptual): Public (health, status), Plugin (process launched by KPM + certificate), Admin (elevated user or trusted process + certificate). So HTTPS is used for synchronous management and integration; MQTT is used for asynchronous event and message delivery between plugins and the job system.


How Plugins are Loaded and Monitored

Loading Plugins

  1. Discovery — At startup, the main service scans the Plugins directory for plugin JSON files (e.g. KeeperPolicy.json, KeeperApi.json). Each file describes one plugin: id, name, executable path, subscription topic(s), startup priority, autoStart, and other metadata.

  2. Validation — The service validates the plugin (e.g. path security, certificate if required). Plugins that fail validation are not started.

  3. Startup order — Plugins are ordered by startupPriority (lower number = start first). autoStart: true plugins are started in that order; autoStart: false plugins are loaded but not started until requested (e.g. via API or on demand).

  4. Launch — For Executable plugins, the service launches the plugin process (the path in executablePath). The process connects to the MQTT broker and, typically, to the HTTP API to load settings. The plugin then subscribes to its configured topics and processes messages.

So plugins are loaded from JSON; they are not “registered” via API at runtime—the JSON files are the source of truth at startup. To add a plugin, you add its JSON (and binaries) and restart the service (or start the plugin via API if it has autoStart: false).

Monitoring and Restart

  • Health checks — The main service periodically checks whether each plugin process is still running and optionally checks liveness (e.g. via a simple ping or health callback). Interval and behavior are configurable (e.g. in PluginMonitoring in appsettings).

  • Auto-restart — If a plugin has autoRestart: true and the monitoring detects that it has exited or is unhealthy, the service can automatically restart it. This keeps critical plugins (e.g. KeeperPolicy, KeeperLogger) running.

  • Manual control — An admin can stop or restart a plugin via the HTTP API (POST /api/plugins/{name}/stop, POST /api/plugins/{name}/restart) without restarting the whole service.

So: plugins are loaded from Plugins/*.json, started in priority order, and monitored; failed plugins can be restarted automatically or manually via the API.


How Jobs are Launched and Executed

Job Definition and Registration

  • Jobs are defined by JSON files in the Jobs directory. Each file has an id, events (optional), schedule (optional), parameters, tasks, condition, and optional alternateJobId.

  • At startup (and when files change, depending on implementation), the job service loads all job JSONs, validates them, and registers each job. A job is “launched” when one of its triggers fires.

Trigger Types

Jobs are launched when one of these happens:

Trigger type
How it fires
Job matches when

Event

A message is published to the event topic with an event name (e.g. PolicyEvaluationPending, LaunchPrivilegeElevation). The job service calls TriggerJobByEvent(eventName, context).

The job’s events array contains an event with that name (e.g. eventType: "Custom", customEvent: "LaunchPrivilegeElevation").

Schedule

An internal timer runs (e.g. every minute or per cron). The job service evaluates each job’s schedule (interval, cron, runAt, calendar).

The current time matches the job’s schedule.

Manual

An admin or script calls POST /api/Jobs/{jobId}/run or POST /api/Jobs/{jobId}/trigger with optional body.

The trigger’s job id matches the job’s id.

A job can have both events and schedule; it runs whenever either an event matches or the schedule matches.

Job Execution Flow (high level)

  1. Trigger fires → Job service finds all jobs that match (by event name, schedule, or job id).

  2. For each matching job (often one), the service starts the job run with the trigger context (event payload, parameters, defaults).

  3. Job-level condition (if defined) is evaluated. If it fails, the job is skipped (or alternateJobId is run). If it passes, the task loop runs.

  4. Task loop — For each task in order:

    • Task condition (if defined) is evaluated against the current context (trigger context + parameters + outputs from previous tasks). If false, the task is skipped and the next task in list order runs.

    • If the task references a plugin setting (e.g. “skip when metadata.redirect.enabled is false”), that is evaluated; if the setting says skip, the task’s command is not run and optional ContextWhenSkipped is merged into context.

    • The task is executed:

      • Service — Run an executable in the service session (e.g. RedirectEvaluator, KeeperMFA). Output (e.g. JSON on stdout) can be merged into context for later tasks.

      • HTTP — Issue an HTTP/HTTPS request (e.g. to the local API to launch a process or trigger another job). No executable.

      • User / UserDesktop / UserElevated — Run in user session (non-elevated, desktop, or elevated).

    • Routing — On success, the next task can be OnSuccess (explicit task id) or the next in list. On failure, the next can be OnFailure or the next in list; if ContinueOnFailure is false, the job stops. If a task is skipped, the next is always the next in list.

  5. When the task list is done (or a task routes to “stop”), the job run completes. No task runs after that unless the job is triggered again.

So jobs are launched by events (MQTT), schedule (timer), or API. Each run executes a sequence of tasks with optional conditions and routing; tasks can run executables, call HTTP, or run in user context, and can publish MQTT or call the API to trigger further jobs or actions.

Task Execution Types (summary)

Type
Where it runs
Typical use

Service

Service session (no user desktop)

Back-end executables (RedirectEvaluator, risk evaluators), scripts, built-in commands (echo, publish-mqtt).

Http

HTTP client

Call local API or external URL (trigger job, launch elevated, revert settings).

User

User session, non-elevated

Process in user context.

UserDesktop

User’s desktop, non-elevated

UI apps (KeeperMFA, KeeperJustification, KeeperApproval) that need to show dialogs.

UserElevated

User context, elevated

Run a process elevated in the user session.


End-to-End Flow Example: Privilege Elevation

To tie MQTT, HTTPS, plugins, and jobs together:

  1. User requests elevation (e.g. right-click “Run as administrator” on an app). The OS or an agent (e.g. KeeperRunAs, hook) captures this and needs a policy decision.

  2. Request to KeeperPolicy — The component that needs the decision publishes a message to the KeeperPolicy topic (or equivalent) with context (user, machine, application, command line, event type).

  3. KeeperPolicy (plugin) receives the message, evaluates policies, and decides ALLOW, DENY, or PENDING (controls required). It publishes the response (e.g. to a response topic or EventMessages).

  4. If the result is PENDING, the job service (or the component that published the event) triggers the PolicyEvaluationPending event with context (ControlList, SessionId, RespondToTopic, etc.). The privilege-elevation-policy-controls (or similar) job matches and runs.

  5. That job runs tasks: e.g. PendingApprovals check, then KeeperMfa / KeeperJustification / KeeperApproval (UserDesktop tasks), then publish-mqtt to send audit and, when controls succeed, publish a LaunchPrivilegeElevation event with context (FilePath, CommandLine, UserName, etc.).

  6. The LaunchPrivilegeElevation job matches that event and runs. Its tasks may include check-redirect (reads RedirectEvaluator setting; runs RedirectEvaluator executable if redirect enabled), then either launch-substitute (HTTP call to launch the substitute exe elevated) or launch-elevated (HTTP call to launch the requested exe elevated), then publish-mqtt to send the final response (e.g. DidElevate or Deny) to the caller.

  7. The caller (e.g. hook or agent) receives the response and allows or blocks the user action accordingly.

Throughout, MQTT carries the events and request/response messages; HTTPS is used for plugin settings, job triggers, and launch API calls; plugins are loaded from JSON and monitored; jobs are triggered by events (or schedule/manual) and run tasks that can invoke executables and HTTP.


Summary

Topic
Summary

Event-driven

Actions are driven by events (MQTT messages). Policy requests, control workflows, and launch flows are all triggered by publishing to the broker; subscribers (plugins, job service) react and run logic or jobs.

MQTT

Embedded broker on port 8675 (localhost). Used for policy request/response, custom events (job triggers), logging, and inter-plugin messaging. Process and topic restrictions apply.

HTTPS

Kestrel on ports 6888 (HTTP) and 6889 (HTTPS), localhost only. Used for health, plugin/job management, settings, registration, launch, and other API operations.

Plugins

Loaded from Plugins/*.json at startup; started by priority; monitored and auto-restarted if configured. They connect to MQTT and optionally call the HTTP API.

Jobs

Defined in Jobs/*.json; launched by events (MQTT), schedule (timer), or manual (API). Each run executes a task list (Service, HTTP, User, UserDesktop, UserElevated) with conditions and routing.

Return to Reference Index

Last updated

Was this helpful?