Skip to main content

Overview

Wazuh Dashboard Plugins are built on the OpenSearch Dashboards plugin platform, which provides a standardized lifecycle and dependency management system. Each plugin is a self-contained module with defined setup, start, and stop phases.

Plugin Lifecycle

Every plugin implements a consistent lifecycle with three main phases:
1

Setup Phase

Occurs during OpenSearch Dashboards initialization. Plugins register routes, UI applications, and configure integrations.
2

Start Phase

Runs after all plugins have completed setup. Services become available, background jobs start, and the plugin is fully operational.
3

Stop Phase

Called during shutdown to clean up resources, close connections, and terminate background processes.

Plugin Manifest

Each plugin declares its metadata, dependencies, and capabilities in opensearch_dashboards.json:
{
  "id": "pluginId",                    // Unique identifier
  "version": "5.0.0-00",              // Plugin version
  "opensearchDashboardsVersion": "...", // Compatible platform version
  "requiredPlugins": [],              // Must be loaded before this plugin
  "optionalPlugins": [],              // Enhanced functionality if available
  "server": true,                     // Has server-side code
  "ui": true,                         // Has client-side code
  "configPath": ["wazuh"]            // Configuration namespace (optional)
}

The Three Core Plugins

1. wazuh-core: Foundation Services

The core plugin provides essential services used by other plugins.

Public Plugin (Browser)

File: plugins/wazuh-core/public/plugin.ts
public async setup(core: CoreSetup): Promise<WazuhCorePluginSetup> {
  // Initialize configuration store
  this._internal.configurationStore = new ConfigurationStore(logger);

  // Register configuration providers
  this._internal.configurationStore.registerProvider(
    EConfigurationProviders.INITIALIZER_CONTEXT,
    new InitializerConfigProvider(this.initializerContext)
  );

  this._internal.configurationStore.registerProvider(
    EConfigurationProviders.PLUGIN_UI_SETTINGS,
    new UISettingsConfigProvider(core.uiSettings)
  );

  // Create configuration facade
  this.services.configuration = new Configuration(
    logger,
    this._internal.configurationStore
  );

  // Create dashboard security service
  this.services.dashboardSecurity = new DashboardSecurity(logger, core.http);
  await this.services.dashboardSecurity.setup();

  return {
    ...this.services,
    utils,
    API_USER_STATUS_RUN_AS,
    SettingsValidator,
  };
}
Services Provided:

Configuration

Multi-provider configuration management with UI settings and initializer context

Dashboard Security

Security platform detection and integration utilities

Utilities

Shared helper functions and constants

Hooks

React hooks for accessing core services

Server Plugin (Node.js)

File: plugins/wazuh-core/server/plugin.ts
public async setup(core: CoreSetup, plugins: PluginSetup) {
  // Register UI settings for advanced configuration
  const uiSettingsDefs = getUiSettingsDefinitions(PLUGIN_SETTINGS);
  core.uiSettings.register(uiSettingsDefs);

  // Initialize services
  this.services.dashboardSecurity = createDashboardSecurity(plugins);
  this.services.configuration = new Configuration(
    this.logger.get('configuration'),
    this._internal.configurationStore
  );
  this.services.manageHosts = new ManageHosts(
    this.logger.get('manage-hosts'),
    this.services.configuration
  );
  this.services.serverAPIClient = new ServerAPIClient(
    this.logger.get('server-api-client'),
    this.services.manageHosts,
    this.services.dashboardSecurity
  );

  // Register context for route handlers
  core.http.registerRouteHandlerContext('wazuh_core', (context, request) => ({
    ...this.services,
    api: {
      client: {
        asInternalUser: this.services.serverAPIClient.asInternalUser,
        asCurrentUser: this.services.serverAPIClient.asScoped(context, request),
      },
    },
  }));

  return { ...this.services, API_USER_STATUS_RUN_AS, api: {...} };
}
Key Components:
ComponentPurpose
ManageHostsMulti-cluster API host management
ServerAPIClientHTTP client for Wazuh Server API with user context
ConfigurationServer-side configuration aggregation
DashboardSecuritySecurity plugin integration

2. wazuh-check-updates: Update Notifications

A lightweight plugin for managing update notifications. File: plugins/wazuh-check-updates/public/plugin.ts
export class WazuhCheckUpdatesPlugin
  implements Plugin<WazuhCheckUpdatesPluginSetup, WazuhCheckUpdatesPluginStart> {
  
  public setup(core: CoreSetup): WazuhCheckUpdatesPluginSetup {
    // No setup operations needed
    return {};
  }

  public start(
    core: CoreStart,
    plugins: AppPluginStartDependencies
  ): WazuhCheckUpdatesPluginStart {
    setCore(core);
    setWazuhCore(plugins.wazuhCore);

    // Export components and services
    return {
      UpdatesNotification,
      getAvailableUpdates,
      DismissNotificationCheck,
    };
  }

  public stop() {}
}
Exports:
  • UpdatesNotification: UI component for displaying update banners
  • DismissNotificationCheck: Component to check dismissal state

3. wazuh (Main): Complete Security Platform

The main plugin provides all user-facing features and security modules.

Public Plugin

File: plugins/main/public/plugin.ts
public async setup(core: CoreSetup, plugins: WazuhSetupPlugins) {
  // Register query languages for search bar
  const languageService = plugins.data.query.queryString.languageService;
  SUPPORTED_LANGUAGES_ARRAY.forEach(language => {
    const languageInstance = languageService.getLanguage(language);
    languageService.registerLanguage({
      ...languageInstance,
      supportedAppNames: [...languageInstance.supportedAppNames, ...appIds],
    });
  });

  // Register all applications
  for (const app of Applications) {
    const { category, id, title, redirectTo, order } = app;

    core.application.register({
      id,
      title,
      order,
      mount: async (params: AppMountParameters) => {
        setWzCurrentAppID(id);
        setWzMainParams(redirectTo());
        initializeInterceptor(core);

        if (this.hideTelemetryBanner) {
          await this.hideTelemetryBanner();
        }

        setScopedHistory(params.history);
        setHeaderActionMenuMounter(params.setHeaderActionMenu);
        NavigationService.getInstance(createHashHistory());

        // Lazy load application bundle
        const { renderApp } = await import('./application');

        setErrorOrchestrator(ErrorOrchestratorService);
        setHttp(core.http);
        setCookies(new Cookies());

        params.element.classList.add('dscAppWrapper', 'wz-app');
        const unmount = await renderApp(params);

        return () => {
          unmount();
          unregisterInterceptor();
        };
      },
      category: Categories.find(({ id: categoryID }) => categoryID === category),
    });
  }

  // Register navigation links for new home page
  registerWazuhNavLinks(
    core.chrome.navGroup.addNavLinksToGroup.bind(core.chrome.navGroup),
    core.chrome.navGroup.getNavGroupEnabled()
  );

  return {};
}
Application Structure: Each application registered by the main plugin represents a security module:
// Example application definition from applications.ts
export const configurationAssessment = {
  category: 'wz-category-endpoint-security',
  id: 'configuration-assessment',
  title: 'Configuration Assessment',
  order: 200,
  euiIconType: 'managementApp',
  showInOverviewApp: true,
  showInAgentMenu: true,
  redirectTo: () => `/overview/?tab=sca&tabView=dashboard${...}`,
};

Server Plugin

File: plugins/main/server/plugin.ts
public async setup(core: CoreSetup, plugins: PluginSetup) {
  // Register custom route handler context
  core.http.registerRouteHandlerContext('wazuh', (context, request) => ({
    logger: this.logger.get(
      `${request.route.method.toUpperCase()} ${request.route.path}`
    ),
    server: { info: serverInfo },
    plugins,
    security: plugins.wazuhCore.dashboardSecurity,
    api: context.wazuh_core.api,
  }));

  // Add security headers
  core.http.registerOnPreResponse((request, response, toolkit) => {
    const additionalHeaders = {
      'x-frame-options': 'sameorigin',
    };
    return toolkit.next({ headers: additionalHeaders });
  });

  // Create router and register routes
  const router = core.http.createRouter();
  setupRoutes(router, plugins.wazuhCore);

  // Register health checks (shown in next section)
  // ...

  return {};
}

Health Check System

The main plugin’s server implements extensive health checks during setup:

Index Pattern Health Checks

Example from plugins/main/server/plugin.ts (lines 261-296):
core.healthCheck.register(
  initializationTaskCreatorIndexPattern({
    services: plugins.wazuhCore,
    taskName: HEALTH_CHECK_TASK_INDEX_PATTERN_AGENTS_MONITORING,
    indexPatternID: WAZUH_MONITORING_PATTERN, // 'wazuh-monitoring*'
    options: {
      savedObjectOverwrite: defineTimeFieldNameIfExist('timestamp'),
      hasTimeFieldName: true,
      fieldsNoIndices: IndexPatternMonitoringKnownFields,
    },
  })
);
The system registers health checks for:
  • System Activity Events
  • Security Events
  • Access Management Events
  • Applications Events
  • Other Events
  • Network Activity Events
  • Cloud Services Events (Generic, AWS, Azure, GCP)
  • Vulnerabilities
  • FIM (Files, Registry Keys, Registry Values)
  • SCA (Security Configuration Assessment)
  • Inventory (System, Hardware, Networks, Packages, Ports, Processes, Protocols, Users, Groups, Services, Interfaces, Hotfixes, Browser Extensions)
  • Monitoring (Agent status and metrics)
  • Statistics (Server performance data)
  • Connection compatibility
  • Run-as configuration validation
  • Required dashboards and visualizations

Plugin Dependencies

Dependency Graph

Required vs Optional Dependencies

Must be present for plugin to load:All plugins require:
  • navigation - Menu and navigation services
  • opensearchDashboardsUtils - Common utilities
Main plugin additionally requires:
  • data - Search and aggregations
  • dashboard - Dashboard embedding
  • discover - Event exploration
  • visualizations - Chart rendering
  • charts - Chart configuration
  • savedObjects - Saved object management
  • wazuhCore - Foundation services
  • wazuhCheckUpdates - Update notifications

Service Architecture

Public Services (Browser)

Services are made globally available through module-level setters:
// Example from plugins/main/public/kibana-services.ts
let http: CoreStart['http'];
let data: DataPublicPluginStart;
let navigation: NavigationPublicPluginStart;

export const setHttp = (h: CoreStart['http']) => { http = h; };
export const getHttp = () => http;

export const setDataPlugin = (d: DataPublicPluginStart) => { data = d; };
export const getDataPlugin = () => data;

export const setNavigationPlugin = (n: NavigationPublicPluginStart) => { navigation = n; };
export const getNavigationPlugin = () => navigation;
Benefits:
  • Avoid prop drilling through deep component trees
  • Access platform services from any component
  • Testable through service mocking

Server Services (Node.js)

Services are provided through request context:
// Accessing services in route handlers
router.get(
  { path: '/api/wazuh/example', validate: false },
  async (context, request, response) => {
    // Core services from wazuh-core
    const apiClient = context.wazuh_core.api.client.asCurrentUser;
    const config = await context.wazuh_core.configuration.get();
    
    // Wazuh-specific context
    const logger = context.wazuh.logger;
    const security = context.wazuh.security;
    
    // Use services
    const apiResponse = await apiClient.request(
      'GET',
      '/agents',
      {},
      { apiHostID: 'default' }
    );
    
    return response.ok({ body: apiResponse });
  }
);

Plugin Communication

Plugins communicate through:

Direct API

Setup/start return values expose services to dependent plugins

Shared Context

Request context provides scoped service access

Events

Core event bus for lifecycle and state events

HTTP API

REST endpoints for cross-plugin communication

Development Workflow

Creating a New Plugin

1

Create Plugin Structure

mkdir -p plugins/my-plugin/{public,server,common}
2

Add Manifest

Create opensearch_dashboards.json with plugin metadata
3

Implement Plugin Classes

Create public/plugin.ts and server/plugin.ts
4

Export Plugin

Create public/index.ts and server/index.ts
5

Build and Test

yarn build
yarn test

Plugin Development Best Practices

Avoid These Anti-Patterns:
  • Don’t access services before they’re initialized
  • Don’t create circular dependencies between plugins
  • Don’t bypass the plugin lifecycle (setup → start → stop)
  • Don’t store request-specific data in plugin instance variables
Follow These Patterns:
  • Use TypeScript interfaces for plugin contracts
  • Implement proper error handling in all lifecycle methods
  • Clean up resources in stop() method
  • Use dependency injection for testability
  • Keep plugins focused on single responsibilities

Architecture

Overall system architecture and design

Security Modules

Implemented security and monitoring features

Data Sources

Index patterns and data organization

Development Guide

Set up your development environment

Build docs developers (and LLMs) love