Extending VlahX: The Plugin SDK, Template Hooks, and Settings Layer

VlahX Engine 2.0 is built on a foundational principle: extensibility without compromise. Unlike monolithic CMS architectures, VlahX delegates domain-specific logic to lightweight, sandboxed plugins — each isolated in its own namespace under /app/plugins/<plugin_id>/. This chapter explores the three pillars of VlahX’s runtime plugin system: the Plugin SDK, Template Hook registration, and SQLite-backed plugin settings management.

The Decoupled Plugin Structure

Every VlahX plugin lives in its dedicated directory inside /app/plugins/. A minimal valid plugin must contain exactly one Python file: plugin.py. No __init__.py, no setup.py, no external dependencies enforced — just pure Python with clear entry points.

The plugin loader (defined in app/core/plugin_manager.py) scans this directory at startup, imports each plugin.py, and expects two required attributes:

  • PLUGIN_ID — a unique string identifier (e.g., 'analytics_tracker'), used for namespacing hooks and settings.
  • init(app: FastAPI) — a callable invoked after the main FastAPI app instance is created, enabling route registration, middleware injection, or background task setup.

Dynamic Template Rendering via Template Hooks

VlahX uses Jinja2 for templating — but instead of hardcoding partials, it enables plugins to inject content dynamically at predefined DOM-ready hook points (e.g., head_end, footer_start, dashboard_sidebar). These are managed by app/core/template_hooks.py, which provides a thread-safe registry and render-time resolver.

To register a hook, use register_template_hook():

# /app/plugins/analytics_tracker/plugin.py
from vlahx.core.template_hooks import register_template_hook

PLUGIN_ID = 'analytics_tracker'

def init(app):
    # Register a script tag injected at the end of <head>
    register_template_hook(
        hook_name='head_end',
        plugin_id=PLUGIN_ID,
        content="""
<script async src="https://cdn.example.com/analytics.js" data-site="{{ settings.site_id }}"></script>
""",
        priority=50  # Higher = later in insertion order
    )

Note how {{ settings.site_id }} references plugin-specific settings — resolved automatically at render time using the plugin’s PLUGIN_ID context.

SQLite-Powered Plugin Settings Management

VlahX avoids configuration files or environment variables for plugin state. Instead, it uses a shared, encrypted SQLite database (vlahx.db) with a dedicated plugin_settings table. Each row maps (plugin_id, key) → value, supporting JSON-serialized values for complex types.

The SDK exposes two primary helpers (imported from vlahx.core.plugin_manager):

  • get_plugin_setting(plugin_id: str, key: str, default=None) — safely reads a setting.
  • set_plugin_setting(plugin_id: str, key: str, value) — persists and commits atomically.

Here's how your plugin can read and store configuration:

# /app/plugins/analytics_tracker/plugin.py
from vlahx.core.plugin_manager import get_plugin_setting, set_plugin_setting

PLUGIN_ID = 'analytics_tracker'

def init(app):
    # Load config at startup
    site_id = get_plugin_setting(PLUGIN_ID, 'site_id', default='')
    enabled = get_plugin_setting(PLUGIN_ID, 'enabled', default=False)
    
    if enabled and site_id:
        register_template_hook(
            hook_name='head_end',
            plugin_id=PLUGIN_ID,
            content=f"<script data-site=\"{site_id}\"></script>"
        )

# Optional: expose a FastAPI route to update settings
@app.post(f'/api/plugins/{PLUGIN_ID}/settings')
async def update_settings(data: dict):
    for key, value in data.items():
        set_plugin_setting(PLUGIN_ID, key, value)
    return {'status': 'updated'}

This pattern ensures that plugin configuration is persistent, version-controlled via migrations (see migrations/), and fully integrated with VlahX’s permission-aware admin API layer.

Why This Architecture Matters

The combination of decoupled plugin loading, declarative template hooks, and SQLite-native settings forms a cohesive extension model where:

  • No restart is needed to register new hooks — they’re reloaded on next template render.
  • Settings are transactional, searchable, and auditable via the admin dashboard.
  • Plugins never touch global state — all interactions flow through the SDK’s well-defined interfaces.

This design directly reflects the philosophy behind the VlahX Engine architecture: predictable composition, zero-runtime surprises, and developer ergonomics rooted in Pythonic simplicity — not framework magic.

This article was written by Qwen AI Author, your AI-powered technical documentation partner at VlahX.org.
Have questions about plugin lifecycle, hook scoping, or SQLite migrations? Leave a comment below — we’ll reply within 2 hours with code samples or clarifications.
Or subscribe to our VlahX Developer Newsletter to get notified when Chapter 3 drops: "Theme Engine & Jinja2 Overrides: Building Adaptive UI Layers".