# INFINI Console Plugin System Architecture: A Deep Dive into Extensibility

> Explore INFINI Console's plugin system architecture. Learn how TypeScript classes and dependency injection deliver modular, decoupled extensibility for your console.

- Repository: [INFINI Labs/console](https://github.com/infinilabs/console)
- Tags: architecture
- Published: 2026-03-04

---

**INFINI Console implements a Kibana-based plugin architecture where TypeScript classes implement a generic `Plugin` interface with lifecycle methods (`setup`, `start`, `stop`) and dependency injection to enable modular, decoupled extensibility.**

INFINI Console, part of the `infinilabs/console` repository, extends the Kibana platform to provide a comprehensive management interface for INFINI Labs products. The INFINI Console plugin system architecture treats plugins as first-class modules, allowing developers to extend functionality without modifying the core codebase through a well-defined lifecycle and contract-based dependency injection.

## Core Plugin Architecture and Lifecycle

At the heart of the system lies the generic `Plugin` interface that every extension must implement. This interface enforces a strict lifecycle through three primary methods: `setup`, `start`, and `stop`.

### The Generic Plugin Interface

Every plugin is a TypeScript class implementing `Plugin<TSetup, TStart, TSetupDeps, TStartDeps>`. The generic parameters define the contracts exposed to other plugins and the dependencies required at each phase:

```typescript
export class MyPlugin implements Plugin<TSetup, TStart, TSetupDeps, TStartDeps> {
  constructor(initializerContext: PluginInitializerContext) { … }
  public setup(core: CoreSetup<TStartDeps, TStart>, deps: TSetupDeps): TSetup { … }
  public start(core: CoreStart, deps: TStartDeps): TStart { … }
  public stop() { … }
}

```

The `initializerContext` provides configuration and logging capabilities during instantiation.

### Lifecycle Phases: Constructor, Setup, Start, and Stop

The platform orchestrates plugins through four distinct phases:

| Phase | What happens | Example source |
|------|--------------|----------------|
| **Constructor** | Plugin instance is created, usually stores the `initializerContext`. | `DataServerPlugin` constructor – [`web/src/components/vendor/data/server/plugin.ts`](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/data/server/plugin.ts) |
| **Setup** | Receives `CoreSetup` and declared **setup dependencies**. Registers UI elements, routes, services, and returns a setup contract. | `IndexPatternManagementPlugin.setup` – [`web/src/components/vendor/index_pattern_management/public/plugin.ts`](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/index_pattern_management/public/plugin.ts) |
| **Start** | Receives `CoreStart` and **start dependencies**. Starts long-running services and returns a start contract. | `DataPublicPlugin.start` – [`web/src/components/vendor/data/public/plugin.ts`](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/data/public/plugin.ts) |
| **Stop** | Cleans up resources (timers, listeners, subscriptions). | `DataServerPlugin.stop` – [`web/src/components/vendor/data/server/plugin.ts`](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/data/server/plugin.ts) |

During **setup**, plugins receive `CoreSetup` containing HTTP configuration, UI settings, and notification services. During **start**, they receive `CoreStart` with runtime services like authenticated HTTP clients and saved objects.

## Dependency Injection and Plugin Contracts

The architecture employs explicit dependency injection through TypeScript interfaces, enabling type-safe communication between plugins without tight coupling.

### Declaring Setup and Start Dependencies

Plugins declare their dependencies using separate interfaces for setup and start phases:

```typescript
export interface IndexPatternManagementSetupDependencies {
  management: ManagementSetup;          // from the management plugin
  urlForwarding: UrlForwardingSetup;    // from the url_forwarding plugin
}

```

During `setup`, the platform passes the concrete objects:

```typescript
setup(core, { management, urlForwarding })

```

(See full implementation in [`web/src/components/vendor/index_pattern_management/public/plugin.ts`](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/index_pattern_management/public/plugin.ts).)

### Contract-Based Communication Between Plugins

The generic `TSetup` and `TStart` parameters define the **contracts** that plugins expose. When a plugin returns an object from `setup()` or `start()`, other plugins can consume these methods through their dependency declarations.

For example, the `DataPublicPlugin` in [`web/src/components/vendor/data/public/plugin.ts`](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/data/public/plugin.ts) exposes search, field formats, and query services through its start contract, allowing dependent plugins to execute searches without knowing the implementation details.

## Extensibility Points in INFINI Console

The plugin system provides specific hooks for extending the UI, services, and data models.

### Application Registration and URL Generation

Plugins register new applications using `core.application.register()` during setup. The `DiscoverPlugin` in [`web/src/components/vendor/discover/public/plugin.ts`](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/discover/public/plugin.ts) demonstrates this by registering the Discover app and providing deep links through URL generators:

```typescript
plugins.share?.urlGenerators.registerUrlGenerator(/* ... */);

```

### Service Registration and Saved Object Types

Server-side plugins register domain-specific services and persistence models. The `DataServerPlugin` in [`web/src/components/vendor/data/server/plugin.ts`](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/data/server/plugin.ts) registers search services and index pattern saved object types, making them available to the client through the start contract.

### UI Actions, Embeddables, and State Containers

The public data plugin in [`web/src/components/vendor/data/public/plugin.ts`](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/data/public/plugin.ts) registers UI actions and triggers using `uiActions.registerAction()`, allowing other plugins to hook into user interactions like filter creation or query execution.

State containers and sync utilities under `vendor/utils/public/state_sync/` enable plugins to share UI state across the application boundary without direct coupling.

## Building a Custom Plugin: Minimal Example

To create a new plugin, implement the `Plugin` interface with explicit contracts and dependency declarations:

```typescript
// src/plugins/my_plugin/public/plugin.ts
import {
  PluginInitializerContext,
  CoreSetup,
  CoreStart,
  Plugin,
} from 'src/core/public';

export interface MySetup {
  hello(): void;
}
export interface MyStart {
  greet(name: string): string;
}
export interface MySetupDeps {
  // e.g. depend on the data plugin's public contract
  data: DataPublicPluginSetup;
}
export interface MyStartDeps {}

export class MyPlugin
  implements Plugin<MySetup, MyStart, MySetupDeps, MyStartDeps>
{
  constructor(context: PluginInitializerContext) {}

  public setup(core: CoreSetup<MyStartDeps, MyStart>, { data }: MySetupDeps): MySetup {
    core.application.register({
      id: 'myPlugin',
      title: 'My Plugin',
      async mount(params) {
        // mount UI here
        return () => {}; // unmount
      },
    });

    return {
      hello() {
        console.log('Hello from MyPlugin!');
      },
    };
  }

  public start(core: CoreStart, _deps: MyStartDeps): MyStart {
    return {
      greet(name) {
        return `Hello, ${name}!`;
      },
    };
  }

  public stop() {}
}

/** Entry point required by the Kibana platform */
export function plugin(initializerContext: PluginInitializerContext) {
  return new MyPlugin(initializerContext);
}

```

**Key implementation details**:

1. **Explicit contracts**: `MySetup` and `MyStart` define the API surface for other plugins.
2. **Dependency injection**: `MySetupDeps` declares the `data` plugin dependency, injected during `setup`.
3. **Lifecycle compliance**: The `plugin()` factory function instantiates the class, satisfying the platform's entry point requirement.

## Key Source Files and Implementation References

The following files demonstrate the plugin architecture in the `infinilabs/console` repository:

| File | Role | Link |
|------|------|------|
| [`web/src/components/vendor/index_pattern_management/public/plugin.ts`](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/index_pattern_management/public/plugin.ts) | Example of a **public UI plugin** that registers a management app and forwards legacy URLs. | [IndexPatternManagementPlugin](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/index_pattern_management/public/plugin.ts) |
| [`web/src/components/vendor/discover/public/plugin.ts`](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/discover/public/plugin.ts) | Shows a **feature‑rich plugin** (Discover) that registers an app, URL generator, doc‑view extensions, and state sync. | [DiscoverPlugin](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/discover/public/plugin.ts) |
| [`web/src/components/vendor/data/server/plugin.ts`](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/data/server/plugin.ts) | Core **server‑side plugin** providing data services (search, field formats, index patterns). | [DataServerPlugin](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/data/server/plugin.ts) |
| [`web/src/components/vendor/data/public/plugin.ts`](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/data/public/plugin.ts) | Companion **public data plugin** exposing services to the UI and other public plugins. | [DataPublicPlugin](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/data/public/plugin.ts) |
| [`src/core/public/plugin.ts`](https://github.com/infinilabs/console/blob/main/src/core/public/plugin.ts) (in the Kibana core – not duplicated here) | Defines the **generic `Plugin` interface** used throughout. | *(core library)* |
| [`src/core/server/plugin.ts`](https://github.com/infinilabs/console/blob/main/src/core/server/plugin.ts) | Server‑side counterpart of the core `Plugin` interface. | *(core library)* |

These files together illustrate the **full lifecycle**, **dependency injection**, and **extensibility hooks** that make INFINI Console a pluggable platform. By following the same pattern—implementing `Plugin`, declaring contracts, registering UI components or services, and exporting a `plugin()` factory—developers can extend the console without modifying the core codebase.

## Summary

- **INFINI Console** extends the Kibana platform, treating plugins as first-class modules with a strict lifecycle.
- The **generic `Plugin` interface** enforces `setup`, `start`, and `stop` methods, with TypeScript generics defining contracts (`TSetup`, `TStart`) and dependencies (`TSetupDeps`, `TStartDeps`).
- **Dependency injection** occurs through explicit interface declarations, allowing type-safe access to other plugins' contracts without tight coupling.
- **Extensibility points** include application registration, URL generators, service exposure, saved object types, UI actions, and state containers.
- Implementation requires extending `Plugin`, exporting a `plugin()` factory, and following the lifecycle pattern demonstrated in [`web/src/components/vendor/data/public/plugin.ts`](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/data/public/plugin.ts) and related files.

## Frequently Asked Questions

### What interface must a plugin implement in INFINI Console?

Every plugin must implement the generic `Plugin<TSetup, TStart, TSetupDeps, TStartDeps>` interface defined in the Kibana core ([`src/core/public/plugin.ts`](https://github.com/infinilabs/console/blob/main/src/core/public/plugin.ts) or [`src/core/server/plugin.ts`](https://github.com/infinilabs/console/blob/main/src/core/server/plugin.ts)). This interface requires three methods: `setup(core, deps)` for bootstrap registration, `start(core, deps)` for runtime initialization, and `stop()` for cleanup. The generic parameters define the contracts the plugin exposes and the dependencies it requires from other plugins.

### How does dependency injection work between plugins?

Dependency injection operates through explicit TypeScript interface declarations. Plugins define `TSetupDeps` and `TStartDeps` interfaces listing required dependencies (e.g., `management: ManagementSetup`). The platform automatically resolves these by matching dependency names to exported contracts from other plugins, injecting them as the second parameter to `setup()` and `start()` methods. This ensures type-safe, decoupled communication where plugins interact only through declared contracts.

### What is the difference between the setup and start phases?

The **setup** phase executes while the platform is booting, before the application becomes fully operational. During setup, plugins register routes, UI applications, saved object types, and return setup contracts. The **start** phase executes after all setup phases complete, when the platform is fully initialized. During start, plugins launch long-running services, establish runtime connections, and return start contracts. Setup focuses on static registration; start focuses on dynamic service activation.

### Can plugins register new applications in the INFINI Console UI?

Yes, plugins can register entirely new applications using `core.application.register()` during the setup phase. This method accepts an application descriptor including a unique ID, title, and async `mount` function that renders the UI. For example, the Discover plugin in [`web/src/components/vendor/discover/public/plugin.ts`](https://github.com/infinilabs/console/blob/main/web/src/components/vendor/discover/public/plugin.ts) registers the Discover application and provides deep linking capabilities through URL generators, demonstrating how plugins can add full-featured UI modules to the console.