# OfficeCLI Plugin Discovery Mechanism: 4-Step Resolution for External Extensions

> Understand the OfficeCLI plugin discovery mechanism. Learn its 4-step resolution process for external extensions, ensuring secure and efficient plugin loading via environment variables, user and bundled directories, and PATH lo...

- Repository: [OfficeAI/OfficeCLI](https://github.com/iofficeai/OfficeCLI)
- Tags: internals
- Published: 2026-07-10

---

**OfficeCLI resolves external plugins through a deterministic four-step hierarchy—environment variables, user directories, bundled directories, and PATH lookups—implemented in [`PluginRegistry.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/PluginRegistry.cs) with strict security checks and manifest verification.**

OfficeCLI extends its native document support through a flexible plugin architecture that enables external handling of additional formats like `.docx`, `.xlsx`, and `.pptx`. The plugin discovery mechanism, formally specified in [`plugins/plugin-protocol.md`](https://github.com/iOfficeAI/OfficeCLI/blob/main/plugins/plugin-protocol.md) and implemented in [`src/officecli/Core/Plugins/PluginRegistry.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Plugins/PluginRegistry.cs), applies a fixed resolution order to locate executables while maintaining security through filesystem validation and performance through intelligent caching.

## The Four-Step Discovery Hierarchy

The host binary locates a plugin for a given **kind** (e.g., `dump-reader`, `exporter`, `format-handler`) and file **extension** by applying a strict priority order defined in the plugin protocol. This algorithm ensures deterministic behavior while allowing multiple override strategies.

### Step 1: Environment Variable Override

The highest priority location is a dedicated environment variable pointing directly to an executable. The variable name follows the pattern `$OFFICECLI_PLUGIN_<KIND>_<EXT>`, where the kind is converted to kebab-case and the extension (without the leading dot) is upper-cased.

```bash

# Explicitly specify a dump-reader plugin for .doc files

export OFFICECLI_PLUGIN_DUMP_READER_DOC=/opt/myplugins/officecli-doc
officecli view report.doc

```

This method bypasses all other discovery mechanisms and is useful for development or testing specific plugin versions.

### Step 2: User Plugin Directory

If no environment variable is set, OfficeCLI checks the per-user well-known location at `~/.officecli/plugins/<kind>/<ext>/plugin(.exe)`. This directory structure supports cross-platform executable extensions automatically.

```bash

# Install a plugin for the current user

mkdir -p ~/.officecli/plugins/dump-reader/doc
cp mydumpreader-plugin ~/.officecli/plugins/dump-reader/doc/plugin

# Verify discovery

officecli plugins list | grep doc

```

### Step 3: Bundled Plugin Directory

The third location searches alongside the main binary in `<app-dir>/plugins/<kind>/<ext>/plugin(.exe)`. This enables application distributors to ship plugins as part of the core package.

```bash

# Assuming the main binary resides at /usr/local/bin/officecli

mkdir -p /usr/local/bin/plugins/exporter/pdf
cp mypdf-exporter /usr/local/bin/plugins/exporter/pdf/plugin

# The plugin is automatically discoverable

officecli view presentation.pptx pdf --out out.pdf

```

### Step 4: PATH Lookup with Security Filtering

Finally, the system searches the `PATH` environment variable for executables named `officecli-<kind>-<ext>` or the shorthand `officecli-<ext>`. Critical security checks prevent execution from relative paths or world-writable directories.

```bash

# Install a binary named officecli-exporter-pdf in PATH

sudo cp officecli-pdf-export /usr/local/bin/officecli-exporter-pdf
officecli view file.docx pdf --out file.pdf

```

## Core Implementation in PluginRegistry.cs

The discovery logic resides in [`src/officecli/Core/Plugins/PluginRegistry.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/Plugins/PluginRegistry.cs), which orchestrates candidate generation, caching, and safety validation according to the protocol specification.

### Candidate Generation via CandidatePaths()

The `CandidatePaths(kind, ext)` method (lines 90-108) yields the four locations in strict priority order. This method serves as the single source of truth for the discovery algorithm, ensuring consistent behavior across the application.

### Process-Wide Caching Strategy

To avoid repeated filesystem probing, the registry maintains a process-wide dictionary `_cache` (lines 24-31) storing both positive and negative hits. When a new plugin is installed programmatically or externally, callers can invalidate the cache by invoking `InvalidateCache()` (lines 52-59), forcing a fresh discovery on the next request.

### Extension Normalization Rules

Before comparison, extensions undergo normalization in lines 28-33 and 35-36: they are forced to lower-case, prefixed with a dot if absent, and compared case-insensitively. This ensures that `.DOC` and `.doc` resolve identically while maintaining internal consistency.

### Security Checks and World-Writable Protection

The `PathCandidates` implementation (lines 71-78) applies strict security filters during PATH traversal. It skips relative directory entries and ignores any directory with the Unix "other-write" bit set, preventing privilege escalation through hijacked executables in world-writable locations.

## Plugin Verification and Manifest Validation

Once a candidate executable is discovered, OfficeCLI verifies its compatibility before execution. The registry invokes `<plugin> --info` with a 5-second timeout to retrieve a JSON manifest describing the plugin's capabilities.

The manifest must declare a `protocol` version matching the host's supported version (currently `1`), along with compatible `kinds` and `extensions` arrays. Only if these values align with the discovery request does the plugin become the resolved result. This verification prevents incompatible or malformed plugins from crashing the host process.

The manifest parsing logic utilizes [`PluginManifest.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/PluginManifest.cs) for deserialization, while [`PluginProcess.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/PluginProcess.cs) manages the plugin's lifecycle after successful discovery, including idle-timeout watchdogs and IPC handling.

## Summary

- **Four-step hierarchy**: OfficeCLI checks environment variables, user directories (`~/.officecli/plugins/`), bundled directories (`<app-dir>/plugins/`), and PATH entries in strict order as defined in [`plugins/plugin-protocol.md`](https://github.com/iOfficeAI/OfficeCLI/blob/main/plugins/plugin-protocol.md).
- **Security first**: The discovery mechanism ignores world-writable directories and relative PATH entries to prevent executable hijacking.
- **Performance optimized**: A process-wide cache in [`PluginRegistry.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/PluginRegistry.cs) stores discovery results, with `InvalidateCache()` available for manual refresh.
- **Strict validation**: Discovered plugins must respond to `--info` with a valid manifest matching protocol version `1` and declaring compatible kinds/extensions.
- **Normalization**: Extensions are normalized to lower-case with dot prefixes before comparison, ensuring case-insensitive matching.

## Frequently Asked Questions

### How does OfficeCLI determine which plugin to use when multiple are installed?

OfficeCLI follows a strict priority order defined in `CandidatePaths()` within [`PluginRegistry.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/PluginRegistry.cs). It checks environment variables first, then user-specific directories, followed by bundled directories, and finally PATH entries. The first valid, verified executable wins, and subsequent locations are ignored unless the cache is invalidated.

### What security measures prevent malicious plugins from executing?

The discovery mechanism implements multiple safeguards. During PATH lookup (step 4), `PathCandidates` skips relative directories and any directory with the Unix "other-write" bit set (lines 71-78). Additionally, every discovered plugin must pass manifest verification by responding to `--info` with a protocol version matching the host's expected version (currently `1`), ensuring only compatible and explicitly designed plugins execute.

### Can I force OfficeCLI to reload plugins without restarting the process?

Yes. The `PluginRegistry` class exposes `InvalidateCache()` (lines 52-59), which clears the internal `_cache` dictionary storing discovery results. Call this method after installing new plugins or modifying existing ones to force a fresh discovery on the next plugin request, eliminating the need to restart the OfficeCLI process.

### Why does my plugin in PATH not get discovered?

First, ensure the executable follows the naming convention `officecli-<kind>-<ext>` or `officecli-<ext>`. Second, verify the containing directory is not world-writable and is not a relative path entry, as [`PluginRegistry.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/PluginRegistry.cs) filters these for security (lines 71-78). Finally, confirm the plugin responds to `--info` with a valid JSON manifest declaring the correct protocol version and supported extensions.