# Plugin: Minimal macOS

**Audience:** Integrators registering a long-running managed plugin on macOS endpoints.

This is the macOS variant of [Plugin: Minimal Windows](/keeperpam/endpoint-privilege-manager/integrations/examples/plugin-minimal-windows.md). The plugin structure and field meanings are identical — the differences are the binary path, `supportedPlatforms`, and macOS-specific signing requirements that are stricter here than on any other platform. Read the Windows page first for the full explanation of each field; this page covers only what changes on macOS.

## The Plugin JSON

```json
{
  "id": "MyBridge",
  "name": "My Bridge",
  "description": "Long-running bridge component.",
  "version": "1.0.0",

  "pluginType": "Executable",
  "executablePath": "bin/MyBridge/MyBridge",
  "supportedPlatforms": ["macOS"],

  "Subscription": {
    "Topic": "MyBridge",
    "Qos": 2,
    "CleanSession": true
  },

  "metadata": {
    "mqttRole": ["subscriber", "publisher"],
    "mqttTopics": {
      "publish": ["KeeperLogger"],
      "subscribe": ["MyBridge", "MyBridge/Commands/+"]
    }
  },

  "startupPriority": 60,
  "autoStart": true,
  "executionContext": "Service",
  "requiresMonitoring": true,
  "autoRestart": true
}
```

## What to Change

<table data-header-hidden="false" data-header-sticky><thead><tr><th width="283.333251953125">Field</th><th>What to Put Here</th></tr></thead><tbody><tr><td><code>id</code></td><td>A stable, unique identifier matching the filename: <code>MyBridge.json</code> for <code>"id": "MyBridge"</code>.</td></tr><tr><td><code>executablePath</code></td><td>Path to the binary relative to the agent root, with no <code>.exe</code> extension. The default macOS agent root is <code>/usr/local/KeeperPrivilegeManager</code> — so <code>bin/MyBridge/MyBridge</code> resolves to <code>/usr/local/KeeperPrivilegeManager/bin/MyBridge/MyBridge</code>. Confirm the install root with your administrator.</td></tr><tr><td><code>supportedPlatforms</code></td><td><code>["macOS"]</code> for a macOS-only plugin.</td></tr><tr><td><code>Subscription.Topic</code></td><td>The primary MQTT topic for this plugin. By convention, use the plugin <code>id</code>.</td></tr><tr><td><code>metadata.mqttTopics.publish</code></td><td>Every topic your binary publishes to. Include <code>KeeperLogger</code> if you publish log messages.</td></tr><tr><td><code>metadata.mqttTopics.subscribe</code></td><td>Every topic your binary subscribes to beyond the primary <code>Subscription.Topic</code>.</td></tr></tbody></table>

**No `.exe` extension.** The binary name in both `executablePath` and the directory path has no extension, identical to Linux.

**File permissions.** The binary must be executable by the agent service account before the orchestrator can launch it:

```bash
chmod +x /usr/local/KeeperPrivilegeManager/bin/MyBridge/MyBridge
```

**Signing and notarization are mandatory.** This is the most important macOS-specific requirement and it applies before the agent ever attempts to launch the binary. Gatekeeper blocks unsigned or unnotarized binaries at the OS level — the orchestrator will not get the chance to register or start a binary that Gatekeeper has quarantined. Sign with an Apple Developer ID certificate and submit for notarization as part of your release process. Unlike job task binaries, plugin binaries are also subject to KEPM's stricter plugin path trust checks, so signing is doubly important here.

After signing, add your certificate thumbprint to `Settings:AlternativeSignatures` in `appsettings.json` if your deployment requires explicit thumbprint registration for MQTT or Plugin Settings access outside an orchestrator-launched context.

**`autoRestart` and launchd.** On macOS, the KEPM agent typically runs as a launchd daemon. If `autoRestart: true` is set and the plugin exits unexpectedly, the orchestrator restarts it independently of launchd. Rapid restart loops will generate log volume and CPU load just as on Linux — ensure your plugin exits cleanly under expected shutdown conditions and emits a clear log message before doing so.

## Before You Deploy

1. **Sign and notarize the binary** before placing it on any macOS endpoint. This must happen before deployment — it cannot be done after the fact on the target machine.
2. Place the binary at the path resolved by `executablePath` and set executable permissions with `chmod +x`.
3. Place the JSON file at `{AgentRoot}/Plugins/MyBridge.json`. The filename must match `id`.
4. Coordinate with your administrator on the supported insertion path for your environment.
5. Restart the agent so the orchestrator picks up the new plugin registration:

```bash
sudo launchctl stop com.keeper.privilegemanager
sudo launchctl start com.keeper.privilegemanager
```

Confirm the launchd service label with your administrator — the exact label may differ depending on how the agent was packaged for your deployment.

## Verify

After the agent restarts, check the agent logs for your plugin name to confirm it started. Then verify Plugin Settings are reachable from inside your binary at startup:

```bash
curl -s https://127.0.0.1:6889/api/PluginSettings/MyBridge \
  --cert /path/to/client.pem \
  --key /path/to/client.key \
  --cacert /path/to/ca.pem
```

This call requires Plugin-tier authentication and will return `403` from an arbitrary shell session. Run the equivalent call from inside your plugin binary as a startup smoke test.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.keeper.io/keeperpam/endpoint-privilege-manager/integrations/examples/plugin-minimal-macos.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
