En vous promenant sur Beamreactor, nous stockons votre IP 48h pour des raisons de sécurité.

Markdown-Reader

Wissensdatenbank › BEAMREACTOR_CMDB

BEAMREACTOR_CMDB

BeamReactor – CMDB (Configuration Management & Message Bus) #

> Version: 1.0

> Date: 2026-03-24

> Audience: Core developers, plugin developers, system integrators

> Scope: Inter-component communication, component registry, owner management

---

1. Overview #

The CMDB is BeamReactor's backbone for inter-component communication. It provides:

  • A message bus for plugins, LLMs, load balancers, and services to exchange data
  • A component registry to declare and track what's running
  • An owner system for multi-tenant and federated deployments
  • Polling (flag-based) and push (handler callback) delivery modes

| File | Role |

|------|------|

| `lib/cmdb/CMDB.class.php` | Core class — all static methods |

| `lib/cmdb/cmdb.sql` | Schema: 4 tables |

Namespace: `Beamreactor\CMDB\CMDB`

---

2. Tables #

| Table | Purpose |

|-------|---------|

| `cmdb_owners` | Owners: sites, clients, organisations |

| `cmdb_registry` | Declared components with dual ownership |

| `cmdb_messages` | Message bus (the queue) |

| `cmdb_subscriptions` | Who listens to what |

Installation #

use Beamreactor\CMDB\CMDB;

CMDB::install();  // Runs cmdb.sql — idempotent (CREATE IF NOT EXISTS)

---

3. Owners #

Owners represent the entities that control components. A component has two optional owners:

| Role | Field | Meaning |

|------|-------|---------|

| Business | `business_owner_id` | Client, contract holder, project sponsor |

| Technical | `technical_owner_id` | Ops team, sysadmin, DevOps |

Owner Types #

| Type | Usage |

|------|-------|

| `local` | This BeamReactor instance |

| `remote` | External site / federated node |

| `client` | End customer / tenant |

| `org` | Organisation / department |

| `system` | System-level (cron, scheduler) |

Creating Owners #

// Business owner (a client)
$client_id = CMDB::createOwner('client', 'acme-corp', 'ACME Corporation',
    'https://acme.example.com/api',  // remote endpoint
    'admin@acme.com',
    ['contract' => 'PRO-2026', 'sla' => '99.9%'],  // metadata
    3  // trust level (high)
);

// Technical owner (ops team)
$ops_id = CMDB::createOwner('local', 'devops-team', 'DevOps Team');

Owner Methods #

| Method | Returns | Description |

|--------|---------|-------------|

| `createOwner($type, $name, ...)` | `int\|false` | Create or update an owner (upsert) |

| `getOwner(int $id)` | `array\|false` | Get owner by ID (decodes metadata JSON) |

| `findOwner($type, $name)` | `array\|false` | Find owner by type + name |

| `listOwners(?$type, ?$status)` | `array` | List owners with optional filters |

| `setOwnerStatus($id, $status)` | `int` | Change status: active, suspended, revoked, pending |

| `getOwnedComponents($id, $role)` | `array` | List components for an owner (business/technical/any) |

Inter-Site Authentication #

Owners can hold an API key for remote authentication:

// Generate and set (store the plain key, only the hash is saved)
$api_key = bin2hex(random_bytes(32));
CMDB::setOwnerApiKey($owner_id, $api_key);

// Verify later
if (CMDB::verifyOwnerApiKey($owner_id, $incoming_key))
{
    // Authenticated
}

Keys are hashed with Argon2ID. The plain key is never stored.

Trust Levels #

| Level | Meaning |

|-------|---------|

| 1 | Maximum trust (internal core) |

| 3 | High trust (known partner) |

| 5 | Standard (default) |

| 7 | Low trust (new/unverified) |

| 9 | Minimal trust (sandboxed) |

---

4. Registry #

Every component (plugin, LLM, service, load balancer) should register itself on startup.

Registering a Component #

CMDB::register(
    'plugin',                          // type
    'xchange',                         // name
    '1.0.0',                           // version
    'Marketplace plugin',              // description
    ['trading', 'rating', 'messaging'],// capabilities
    ['max_photos' => 5],              // config
    $client_id,                        // business owner
    $ops_id                            // technical owner
);

Component Types #

| Type | Usage |

|------|-------|

| `plugin` | BeamReactor plugin |

| `llm` | LLM connector (Aegis, GPT, Claude...) |

| `lb` | Load balancer instance |

| `service` | Background service, daemon |

| `core` | BeamReactor core component |

Registry Methods #

| Method | Returns | Description |

|--------|---------|-------------|

| `register($type, $name, ...)` | `int\|false` | Register component (upsert) |

| `getComponent($type, $name)` | `array\|false` | Get component with owner info (JOIN) |

| `listComponents(?$type, ?$status)` | `array` | List with optional filters |

| `hasCapability($type, $name, $cap)` | `bool` | Check if component declares a capability |

| `setStatus($type, $name, $status)` | `int` | Update status |

| `heartbeat($type, $name)` | `int` | Update last_heartbeat timestamp |

| `getStaleComponents($minutes)` | `array` | Find components with no heartbeat since N minutes |

Component Identifier Format #

Components are referenced as `type:name` strings throughout the message bus:

$id = CMDB::componentId('plugin', 'xchange');  // "plugin:xchange"
$parts = CMDB::parseComponentId($id);           // ['type' => 'plugin', 'name' => 'xchange']

---

5. Messages #

The message bus uses a simple queue with flags and priorities.

Sending a Message #

CMDB::send(
    'plugin:xchange',       // source
    'plugin:messaging',     // target
    'xchange.first_contact',// channel
    [                        // payload (JSON)
        'buyer_id'   => 42,
        'seller_id'  => 7,
        'product_id' => 123,
        'message'    => 'Interested in your item'
    ],
    'notify',               // action (optional)
    CMDB::PRIORITY_NORMAL,  // priority (optional, default 5)
    3600,                   // TTL in seconds (optional, null = permanent)
    42                      // user_id (optional)
);

Request/Response Pattern #

For tracked exchanges with automatic correlation:

// Sender: create a request
$req = CMDB::request('plugin:xchange', 'plugin:messaging', 'xchange.first_contact', [
    'buyer_id' => 42,
    'seller_id' => 7,
]);
// $req = ['message_id' => 15, 'correlation_id' => 'a3f8...']

// Receiver: reply to it
CMDB::reply($req['message_id'], ['status' => 'conversation_opened']);

// Sender: check for responses
$responses = CMDB::getResponses($req['correlation_id'], 'plugin:xchange');

Polling (Receiver Side) #

// Get all pending messages
$messages = CMDB::poll('plugin:messaging');

// Filter by channel pattern
$messages = CMDB::poll('plugin:messaging', 'xchange.%');

// Quick checks
if (CMDB::hasPending('plugin:messaging'))
{
    $count = CMDB::countPending('plugin:messaging');
}

`poll()` automatically marks messages as READ and sets status to `delivered`.

Priority Constants #

| Constant | Value | Use |

|----------|-------|-----|

| `PRIORITY_CRITICAL` | 1 | System failure, security alert |

| `PRIORITY_HIGH` | 3 | Needs prompt attention |

| `PRIORITY_NORMAL` | 5 | Default |

| `PRIORITY_LOW` | 7 | Background / informational |

| `PRIORITY_IDLE` | 9 | Cleanup, analytics |

Messages are delivered in priority order (1 first), then by creation date.

---

6. Flags #

Old-school bitfield system for message state tracking.

Flag Constants #

| Constant | Value | Meaning |

|----------|-------|---------|

| `FLAG_READ` | 1 | Message has been seen |

| `FLAG_ACK` | 2 | Receiver acknowledged |

| `FLAG_PROCESSED` | 4 | Action completed |

| `FLAG_ERROR` | 8 | Processing failed |

| `FLAG_EXPIRED` | 16 | TTL exceeded |

Usage #

// Set a flag
CMDB::setFlag($message_id, CMDB::FLAG_READ);

// Set multiple flags at once
CMDB::setFlag($message_id, CMDB::FLAG_ACK | CMDB::FLAG_PROCESSED);

// Clear a flag
CMDB::clearFlag($message_id, CMDB::FLAG_ERROR);

// Check flags (on a fetched message)
if (CMDB::hasFlag($msg['flags'], CMDB::FLAG_ACK))
{
    // Already acknowledged
}

// Shorthand: acknowledge = ACK + PROCESSED + status update
CMDB::acknowledge($message_id);

// Shorthand: mark as failed with error info
CMDB::fail($message_id, 'Connection timeout to remote API');

---

7. Subscriptions #

Components can subscribe to message channels for push or poll delivery.

Subscribing #

// Poll mode (default) — component fetches messages with poll()
CMDB::subscribe('plugin:messaging', 'xchange.*');

// Push mode — handler is called immediately when a matching message arrives
CMDB::subscribe('plugin:messaging', 'xchange.first_contact', 'push',
    'plugins/messaging/handlers/cmdb_push.handler.php'
);

// Both modes
CMDB::subscribe('plugin:monitoring', 'llm.*', 'both',
    'plugins/monitoring/handlers/cmdb_push.handler.php',
    null,  // source filter (null = all sources)
    3      // priority_min: only priority 1-3
);

Push Handlers #

Push handler files are included when a matching message is sent. They receive:

| Variable | Type | Description |

|----------|------|-------------|

| `$cmdb_push_message_id` | `int` | ID of the message that triggered the push |

| `$cmdb_push_subscriber` | `string` | Subscriber component ID |

<?php
// plugins/messaging/handlers/cmdb_push.handler.php
use Beamreactor\CMDB\CMDB;

$msg = CMDB::getMessage($cmdb_push_message_id);

if ($msg && $msg['channel'] === 'xchange.first_contact')
{
    // Create internal conversation between buyer and seller
    $payload = $msg['payload'];
    // ... create_conversation($payload['buyer_id'], $payload['seller_id']);

    CMDB::acknowledge($cmdb_push_message_id);
}
?>

Channel Patterns #

Subscriptions support wildcard matching:

| Pattern | Matches |

|---------|---------|

| `xchange.*` | `xchange.first_contact`, `xchange.rating`, etc. |

| `llm.health` | Only `llm.health` |

| `*.error` | `xchange.error`, `llm.error`, etc. |

Subscription Methods #

| Method | Returns | Description |

|--------|---------|-------------|

| `subscribe($subscriber, $pattern, ...)` | `int\|false` | Subscribe (upsert) |

| `unsubscribe($subscriber, $pattern)` | `int` | Remove subscription |

| `getSubscriptions($subscriber)` | `array` | List active subscriptions |

---

8. Maintenance #

TTL Expiration #

Messages with a `ttl` have an `expires_at` computed at creation. Expired messages must be swept:

// Run periodically (cron / scheduler)
$expired = CMDB::expireMessages();

Purge Old Messages #

// Delete processed/expired/failed messages older than 30 days
$deleted = CMDB::purge(30);

Stale Component Detection #

// Components that haven't sent a heartbeat in 30 minutes
$stale = CMDB::getStaleComponents(30);

foreach ($stale as $component)
{
    CMDB::setStatus($component['component_type'], $component['component_name'], 'degraded');
}

---

9. Typical Plugin Integration #

A plugin registers itself and subscribes during its initialization (`conf/{name}.conf.php` or autoload):

use Beamreactor\CMDB\CMDB;

// Register
CMDB::register('plugin', 'xchange', '1.0.0', 'Marketplace', ['trading', 'rating']);

// Subscribe to responses
CMDB::subscribe('plugin:xchange', 'xchange.*', 'push',
    'plugins/xchange/handlers/cmdb_push.handler.php'
);

// Heartbeat (if the plugin runs as a service)
CMDB::heartbeat('plugin', 'xchange');

Sending Inter-Plugin Messages #

// xchange triggers a messaging conversation
CMDB::send('plugin:xchange', 'plugin:messaging', 'xchange.first_contact', [
    'buyer_id'   => $buyer_id,
    'seller_id'  => $seller_id,
    'product_id' => $product_id,
    'subject'    => 'Interested in: ' . $product_title,
]);

Receiving Messages #

// In a handler or cron job
$messages = CMDB::poll('plugin:xchange', 'messaging.%');

foreach ($messages as $msg)
{
    // Process
    CMDB::acknowledge($msg['id']);
}

---

10. Migrations #

The CMDB includes a full SQL migration system. Each component (plugin, lib, core) can have versioned SQL files that are tracked, checksummed, and never replayed.

File Convention #

plugins/xchange/sql/migrations/001_initial.sql
plugins/xchange/sql/migrations/002_add_trades_table.sql
plugins/xchange/sql/migrations/003_add_rating_index.sql
lib/cmdb/sql/migrations/001_initial.sql

Files must be named with a sortable prefix. They are executed in alphabetical order.

Optional Description Header #

Place a description in the first SQL comment line:

-- Description: Add trades table and buyer/seller indexes
CREATE TABLE IF NOT EXISTS xchange_trades ( ... );

Running Migrations #

// Single component
$result = CMDB::migrate(
    'plugin',                                    // component type
    'xchange',                                   // component name
    'plugins/xchange/sql/migrations',            // migrations directory
    '1.2.0',                                     // current version (optional)
    'admin',                                     // who runs this
    'production'                                 // environment tag
);
// $result = ['applied' => 2, 'skipped' => 1, 'failed' => 0, 'errors' => []]

// All components at once (scans lib/*, plugins/*)
$results = CMDB::migrateAll('deploy-server', 'production');
// $results = ['plugin:xchange' => [...], 'lib:cmdb' => [...], ...]

Checking Status #

// What migrations have been applied?
$status = CMDB::getMigrationStatus('plugin', 'xchange');
// Returns: migration_id, version, status, description, statements_count,
//          execution_time_ms, applied_by, environment, batch, applied_at

// What's pending?
$pending = CMDB::getPendingMigrations('plugin', 'xchange', 'plugins/xchange/sql/migrations');
// Returns: ['003_add_rating_index.sql']

Integrity Check #

Detects if migration files were modified after being applied (checksum mismatch) or deleted:

$tampered = CMDB::verifyMigrationIntegrity();

foreach ($tampered as $t)
{
    echo $t['component'] . ' / ' . $t['file'] . ': ' . $t['issue'];
    // "plugin:xchange / 001_initial.sql: checksum_mismatch"
}

Behavior #

| Scenario | Behavior |

|----------|----------|

| File already applied | Skipped (checksum verified) |

| New file found | Executed in transaction, recorded |

| Statement fails | Transaction rolled back, status = `failed`, migration stops |

| File modified after apply | Warning in `verifyMigrationIntegrity()` |

| File deleted after apply | Reported as `file_missing` |

Batch Numbers #

All migrations executed in a single `migrate()` or `migrateAll()` call share the same batch number. This allows easy rollback identification.

Integration with Deployment #

On `devarea.beamreactor.com`, after syncing PHP files:

// Auto-run all pending migrations
$results = CMDB::migrateAll('devarea-deploy', 'production');

foreach ($results as $component => $r)
{
    if ($r['failed'] > 0)
    {
        // Alert: migration failed for $component
        error_log("Migration failed: $component — " . implode(', ', $r['errors']));
    }
}

---

11. Architecture Diagram #

                        ┌─────────────┐
                        │ cmdb_owners │
                        │ (sites,     │
                        │  clients)   │
                        └──────┬──────┘
                   business_owner_id │ technical_owner_id
                               │
┌──────────────┐       ┌──────┴──────┐       ┌───────────────────┐
│   Plugin A   │──send─▶│ cmdb_messages│◀─poll──│    Plugin B       │
│ (xchange)    │       │  (the bus)  │       │ (messaging)       │
└──────────────┘       └──────┬──────┘       └───────────────────┘
       │                      │                        ▲
       │               ┌──────┴──────┐                 │
       └──register────▶│cmdb_registry│    ┌────────────┴──────┐
                       │ (who exists)│    │cmdb_subscriptions │
                       └─────────────┘    │ (who listens)     │
                                          └───────────────────┘

---

11. Quick Reference #

Most Used Methods #

// Owners
CMDB::createOwner($type, $name, $display, $url, $email, $meta, $trust);
CMDB::getOwnedComponents($owner_id, 'business');

// Registry
CMDB::register($type, $name, $version, $desc, $caps, $config, $biz_owner, $tech_owner);
CMDB::getComponent($type, $name);
CMDB::hasCapability($type, $name, 'rating');

// Messages
CMDB::send($source, $target, $channel, $payload, $action, $priority, $ttl);
CMDB::poll($target, $channel_filter);
CMDB::request($source, $target, $channel, $payload);
CMDB::reply($message_id, $payload);
CMDB::acknowledge($message_id);
CMDB::fail($message_id, $error);

// Subscriptions
CMDB::subscribe($subscriber, $pattern, 'push', $handler);
CMDB::unsubscribe($subscriber, $pattern);

// Maintenance
CMDB::expireMessages();
CMDB::purge(30);
CMDB::getStaleComponents(30);

// Migrations
CMDB::migrate($type, $name, $dir, $version, $who, $env);
CMDB::migrateAll($who, $env);
CMDB::getMigrationStatus($type, $name);
CMDB::getPendingMigrations($type, $name, $dir);
CMDB::verifyMigrationIntegrity();
de en fr