Plugin Architecture¶
OpenClaw Enterprise is built entirely as plugins on top of the OpenClaw platform. This document explains the architecture, the plugin API, the dependency graph, and how plugins communicate with each other.
Everything Is a Plugin¶
The first principle in the project constitution is Upstream First: we extend OpenClaw via plugins; we never fork it. Every enterprise capability -- policy evaluation, audit logging, connectors, task intelligence, auto-response -- is implemented as an OpenClaw plugin.
If a capability cannot be built as a plugin, the design must be reworked or the required extension point must be proposed upstream. There are no exceptions.
The OpenClaw Plugin API¶
Every plugin exports an activate(api) function that receives an OpenClawPluginAPI object. This API provides six registration methods:
registerTool¶
Adds a tool that the AI agent can invoke. Tools appear in the agent's tool list and can be called during conversations.
api.registerTool({
name: 'email_read',
description: 'Fetch a specific email by message ID from Gmail.',
parameters: {
type: 'object',
properties: {
messageId: { type: 'string', description: 'The Gmail message ID to fetch' },
},
required: ['messageId'],
},
execute: async (params) => {
// Tool implementation
},
});
registerHook¶
Attaches logic to lifecycle events in the OpenClaw runtime. Hooks fire at defined points such as before_model_resolve, before_prompt_build, or sessions_send.
api.registerHook({
event: 'before_tool_execute',
priority: 10,
handler: async (context) => {
// Evaluate policy before any tool runs
const result = await evaluator.evaluate(context);
if (result.decision === 'deny') {
throw new PolicyDeniedError(context.tool, result.policyApplied, result.reason);
}
},
});
registerService¶
Registers a long-running background service with start/stop lifecycle management and health checks.
api.registerService({
name: 'policy-hot-reload',
start: () => watcher.start(),
stop: () => watcher.stop(),
healthCheck: () => watcher.healthCheck(),
});
registerHttpRoute¶
Exposes a REST endpoint under the gateway's HTTP server. All enterprise admin APIs use this registration method.
api.registerHttpRoute({
method: 'GET',
path: '/api/v1/audit',
handler: async (req, res) => {
// Handle HTTP request
},
});
registerGatewayMethod¶
Registers a method that other plugins can call through the gateway's inter-plugin communication system. This is the primary mechanism for plugin-to-plugin interaction.
api.registerGatewayMethod({
name: 'policy.evaluate',
handler: async (params) => evaluator.evaluate(params),
});
registerContextEngine¶
Injects context into the agent's prompt construction pipeline. Context engines provide additional information that the agent sees when building its response.
api.registerContextEngine({
name: 'enterprise-context',
provide: async (session) => {
return { policies: activePolicies, userScope: session.user.scope };
},
});
Plugin + Skill Pairs¶
Every plugin has a paired SKILL.md file. The plugin registers tools, services, hooks, and routes (the platform capability). The skill teaches the agent when and how to use them (the behavioral instructions).
Neither is complete without the other:
- A plugin without a skill provides tools the agent does not know how to use.
- A skill without a plugin references tools that do not exist.
The SKILL.md lives at the root of each plugin directory:
Dependency Graph¶
All plugins depend on shared for types, constants, and error classes. All plugins (except shared itself) depend on policy-engine for policy evaluation and data classification. The dependency graph flows in one direction:
graph TD
shared["shared (types, constants, errors, connector-base, health)"]
policy["policy-engine"]
audit["audit-enterprise"]
auth["auth-enterprise"]
gmail["connector-gmail"]
gcal["connector-gcal"]
jira["connector-jira"]
github["connector-github"]
gdrive["connector-gdrive"]
task["task-intelligence"]
auto["auto-response"]
work["work-tracking"]
ocip["ocip-protocol"]
org["org-intelligence"]
viz["visualization"]
shared --> policy
shared --> audit
policy --> audit
policy --> auth
policy --> gmail
policy --> gcal
policy --> jira
policy --> github
policy --> gdrive
policy --> task
policy --> auto
policy --> work
policy --> ocip
policy --> org
policy --> viz
audit --> gmail
audit --> gcal
audit --> jira
audit --> github
audit --> gdrive
audit --> task
audit --> auto
audit --> work
audit --> ocip
audit --> org
jira --> work
Note: Arrows point from dependency to dependent. Every connector and feature plugin depends on both
policy-engineandaudit-enterprisevia GatewayMethods (policy.evaluate,policy.classify,audit.log).
GatewayMethods: Inter-Plugin Communication¶
Plugins do not call each other directly. All inter-plugin communication goes through GatewayMethods -- named methods registered by one plugin and invoked by others through the gateway event system.
Core GatewayMethods¶
| Method | Registered By | Purpose |
|---|---|---|
policy.evaluate |
policy-engine | Evaluate a policy decision (allow/deny/require_approval) |
policy.classify |
policy-engine | Classify data by sensitivity level |
audit.log |
audit-enterprise | Write an immutable audit log entry |
audit.query |
audit-enterprise | Query audit log entries with filters |
auto-response.listPending |
auto-response | List pending approval queue items |
auto-response.approve |
auto-response | Approve a pending auto-response |
auto-response.reject |
auto-response | Reject a pending auto-response |
auto-response.getSummary |
auto-response | Get auto-response summary for briefings |
auto-response.updateScopeConfig |
auto-response | Update scope configuration from policy |
GatewayMethods Type Interface¶
The GatewayMethods interface in plugins/shared/src/connector-base.ts defines the type contract for the core gateway methods that every connector uses:
export interface GatewayMethods {
'policy.evaluate': (params: PolicyEvaluateRequest) => Promise<PolicyEvaluateResponse>;
'policy.classify': (params: ClassifyRequest) => Promise<ClassifyResponse>;
'audit.log': (params: Record<string, unknown>) => Promise<{ id: string }>;
}
Usage Pattern¶
A plugin calls a GatewayMethod through the injected gateway object:
const result = await gateway['policy.evaluate']({
tenantId: 'acme-corp',
userId: 'user-123',
action: 'email_read',
context: {
dataClassification: 'internal',
targetSystem: 'gmail',
},
});
if (result.decision === 'deny') {
// Handle denial
}
Plugin Lifecycle¶
Every plugin goes through a defined lifecycle:
- Load -- The runtime discovers and loads the plugin module.
- Initialize -- The
activate(api)function is called with the plugin API. - Register -- The plugin registers its tools, hooks, services, routes, gateway methods, and context engines.
- Running -- Services are started. Tools are available to the agent. Hooks fire on lifecycle events. HTTP routes accept requests.
- Shutdown -- Services are stopped gracefully. Resources are cleaned up.
The policy-engine plugin loads first, followed by audit-enterprise, then all other plugins. This ordering ensures that policy evaluation and audit logging are available before any other plugin attempts to use them.
The 15 Enterprise Plugins¶
| Plugin | Purpose | Key Registrations |
|---|---|---|
| policy-engine | Central policy authority; OPA evaluation, hierarchy resolution, classification, hot-reload | GatewayMethods: policy.evaluate, policy.classify. Hook: before_tool_execute. Service: policy-hot-reload. |
| audit-enterprise | Append-only audit logging, query, export | GatewayMethods: audit.log, audit.query. HTTP routes: /api/v1/audit. |
| auth-enterprise | SSO/OIDC validation, RBAC mapping, admin API | HTTP routes: /api/v1/auth/*. |
| connector-gmail | Gmail integration (read, search, inbox polling) | Tools: email_read, email_search. Service: gmail-inbox-poller. |
| connector-gcal | Google Calendar integration | Tools: calendar_list, calendar_search. Service: gcal-poller. |
| connector-jira | Jira integration (read, write, webhook) | Tools: jira_read, jira_search, jira_update. |
| connector-github | GitHub integration (PRs, issues, commits) | Tools: github_pr_list, github_issue_search. |
| connector-gdrive | Google Drive integration | Tools: gdrive_search, gdrive_read. |
| task-intelligence | Cross-system task discovery, correlation, prioritization, briefing | Tools: task_list, daily_briefing. Services: task scanner, correlator. |
| auto-response | Message classification, policy-governed auto-responses, approval queue | Hook: incoming message classification. GatewayMethods: approval queue. HTTP routes: /api/v1/auto-response/*. |
| work-tracking | PR-to-Jira correlation, ticket auto-update, standup generation | Tools: standup_summary. Hooks: GitHub webhook events. |
| ocip-protocol | Agent-to-agent exchanges (OCIP envelope, classification filter, loop prevention) | Hooks: sessions_send (outgoing/incoming OCIP envelope). |
| org-intelligence | News aggregation, document monitoring, consistency checking | Tools: org news, doc change alerts. |
| visualization | D3.js dependency graphs, Eisenhower matrix, mind maps via Canvas | Tools: visualization rendering. |
Project Structure¶
Each plugin follows a consistent directory structure:
plugins/{name}/
src/
plugin.ts # Entry point: activate(api) function
openclaw-types.ts # OpenClawPluginAPI type definition (local)
tools/ # Tool implementations
read.ts
write.ts
services/ # Background service implementations
poller.ts
hooks.ts # Hook implementations
routes.ts # HTTP route handlers
tests/
{name}.test.ts # Vitest unit tests
SKILL.md # Paired skill for the agent
README.md # Plugin documentation
package.json # pnpm workspace package
tsconfig.json # Extends tsconfig.base.json
The shared library has a simpler structure:
plugins/shared/
src/
types.ts # All shared type definitions
constants.ts # Shared constants
errors.ts # Error class hierarchy
connector-base.ts # ConnectorBase abstract class + GatewayMethods
health.ts # Health check utilities
index.ts # Re-exports
package.json
tsconfig.json
The K8s operator is a separate Go project:
operator/
api/v1/
types.go # CRD type definitions (OpenClawInstance, PolicyBundle)
groupversion_info.go
zz_generated.deepcopy.go
internal/
controller/
instance_controller.go
policy_controller.go
webhook/
policy_webhook.go
cmd/manager/
main.go
tests/
instance_controller_test.go
policy_controller_test.go