Checkstack Documentation

Plugin Architecture Overview

Introduction

Checkstack is built on a pluggable architecture that enables extensibility, modularity, and flexible deployment options. Everything beyond the core framework is implemented as a plugin, allowing the system to scale from monolithic deployments to distributed microservices.

Core Principles

1. Runtime Registration

Plugins MUST be registerable at runtime. This design enables:

2. Inversion of Control (IoC)

Plugins register themselves with the core application through well-defined interfaces:

The core calls plugin registration functions, not the other way around.

3. Secure Service-to-Service Communication

All plugin-to-plugin communication happens via:

This ensures security even in distributed deployments.

4. Modular Project Structure

Each plugin is a standalone npm package that can:

Project Structure

checkstack/
├── core/
│   ├── backend/           # Core backend framework
│   ├── frontend/          # Core frontend framework
│   ├── backend-api/       # Backend plugin API
│   ├── frontend-api/      # Frontend plugin API
│   ├── common/            # Shared core types
│   ├── ui/                # Shared UI components
│   │
│   ├── auth-*/            # Authentication (essential)
│   ├── catalog-*/         # Entity management (essential)
│   ├── notification-*/    # Notifications (essential)
│   ├── healthcheck-*/     # Health monitoring (essential)
│   ├── queue-*/           # Queue abstraction (essential)
│   └── theme-*/           # UI theming (essential)
│
└── plugins/               # Replaceable providers only
    ├── auth-github-backend/       # GitHub OAuth provider
    ├── auth-credential-backend/   # Username/password auth
    ├── auth-ldap-backend/         # LDAP auth provider
    ├── queue-bullmq-*/            # BullMQ implementation
    ├── queue-memory-*/            # In-memory implementation
    └── healthcheck-http-backend/  # HTTP health strategy

Note: See Packages vs Plugins Architecture for decision criteria on when to create a package vs a plugin.

Package Types

Checkstack uses a strict package type system to maintain clean architecture:

Package Type Suffix/Pattern Purpose Can Depend On
Backend -backend REST APIs, business logic, database Backend packages, common packages
Frontend -frontend UI components, pages, routing Frontend packages, common packages
Common -common Shared types, access rules, constants Common packages only
Node -node Backend-only shared code Backend packages, common packages
React -react Frontend-only shared components Frontend packages, common packages

Dependency Rules

These rules are automatically enforced by the dependency linter:

See dependency-linter.md for details.

Plugin Lifecycle

Backend Plugin Lifecycle

Backend plugins use a two-phase initialization to ensure cross-plugin communication works correctly:

graph TD
    A[Plugin Discovery] --> B[Load Plugin Module]
    B --> C[Create Plugin Schema]
    C --> D[Run Migrations]
    D --> E[Call register function]
    E --> F[Register Access Rules]
    E --> G[Register Services]
    E --> H[Register Extension Points]
    E --> I[Register Init Function]
    
    subgraph "Phase 2: Init"
        I --> J[Resolve Dependencies]
        J --> K[Call init - Register routers]
    end
    
    subgraph "Phase 3: After Plugins Ready"
        K --> L[All Plugins Initialized]
        L --> M[Call afterPluginsReady]
        M --> N[Cross-plugin RPC + Hooks]
    end
    
    N --> O[Plugin Active]

Key Point: The init function registers routers and services. The afterPluginsReady callback runs after ALL plugins have initialized, making it safe to:

Frontend Plugin Lifecycle

graph TD
    A[Plugin Discovery] --> B[Load Plugin Module]
    B --> C[Register APIs]
    C --> D[Register Routes]
    D --> E[Register Nav Items]
    E --> F[Register Extensions]
    F --> G[Plugin Active]

Database Isolation

Each backend plugin gets its own isolated PostgreSQL schema:

Database: checkstack
├── Schema: public (core only)
├── Schema: plugin_catalog-backend
├── Schema: plugin_auth-backend
└── Schema: plugin_healthcheck-backend

Benefits

See drizzle-schema-isolation.md for implementation details.

Extension Points

Extension points enable plugins to provide implementations for core functionality:

Backend Extension Points

Frontend Extension Points

See extension-points.md for detailed documentation.

Configuration Management

Plugins use versioned configurations to support backward compatibility:

interface VersionedConfig<T> {
  version: number;
  pluginId: string;
  data: T;
  migratedAt?: Date;
  originalVersion?: number;
}

This enables:

See versioned-configs.md for details.

Communication Patterns

Frontend ↔ Backend

sequenceDiagram
    participant F as Frontend Plugin
    participant FA as Fetch API
    participant R as Router
    participant B as Backend Plugin
    
    F->>FA: Request with credentials
    FA->>R: HTTPS + JWT
    R->>R: Validate JWT
    R->>R: Check access
    R->>B: Route to plugin
    B->>R: Response
    R->>FA: JSON response
    FA->>F: Typed data

Backend ↔ Backend

sequenceDiagram
    participant P1 as Plugin A
    participant S as Service Registry
    participant P2 as Plugin B
    
    P1->>S: Get service reference
    S->>P1: Service instance
    P1->>P2: Call via HTTPS + JWT
    P2->>P2: Validate service token
    P2->>P1: Response

Access System

Access rules are defined in common packages and registered by backend plugins:

// In catalog-common
export const access = {
  entityRead: {
    id: "entity.read",
    description: "Read Systems and Groups",
  },
} satisfies Record<string, AccessRule>;

// In catalog-backend
env.registerAccessRules(accessRuleList);

// In catalog-frontend
const canRead = accessApi.useAccess(access.entityRead.id);

The core automatically prefixes access rules with the plugin ID: catalog.entity.read

Technology Stack

Backend

Frontend

Deployment Options

Monolith (Default)

All plugins run in a single process:

bun run dev

Microservices

Each plugin can run independently:

# Terminal 1
bun run dev:backend --plugins=catalog-backend

# Terminal 2
bun run dev:backend --plugins=auth-backend

# Terminal 3
bun run dev:frontend

Hybrid

Mix and match based on scaling needs:

Next Steps