Configuration Management Guide

This guide explains how to properly use the configuration system when developing new components (interfaces, plugins, Cortex engines) for Synthetic Heart.

⚠️ IMPORTANT: Use ConfigVar for Global Variables

When declaring configuration variables at module level (global variables), always use `config_registry.get_var()` instead of `config_registry.get_value()`**.

Why ConfigVar?

  • Auto-updating: ConfigVar automatically reflects database changes without manual listeners

  • No boilerplate: No need to write listener functions or update globals manually

  • Developer-friendly: Simpler code, less error-prone

  • Consistent behavior: Works the same way across all components

The Standard Pattern

from core.config_manager import config_registry

# Declare configuration using get_var() - this returns a ConfigVar object
MY_VAR = config_registry.get_var(
    "MY_KEY",
    "default_value",
    label="My Variable",
    description="Description for the WebUI",
    group="My Category",
    component="my_component",
    value_type="string"  # or "int", "bool", "json"
)

# Use the variable naturally - it auto-updates when DB changes
def some_function():
    if MY_VAR:  # ConfigVar supports __bool__
        print(f"Value is: {MY_VAR}")  # Works with string formatting
        return str(MY_VAR)  # Or explicit conversion

How It Works

  1. At import time: get_var() creates a ConfigVar proxy

  2. During initialization: Core registers all variables

  3. After DB load: Core calls notify_all_listeners() automatically

  4. When accessed: ConfigVar returns the current value transparently

ConfigVar API

ConfigVar objects support natural Python operations:

TOKEN = config_registry.get_var("TOKEN", "", ...)

# Boolean check
if TOKEN:  # True if value exists and is not empty
    ...

# String operations
print(f"Token: {TOKEN}")  # Automatic string conversion

# Comparisons
if TOKEN == "expected":
    ...

# Fallback pattern
def get_token():
    """Get token with fallback logic."""
    return str(TOKEN or "default")  # ConfigVar supports __or__

Migration from Old Pattern

If you have existing code using the old pattern:

# Old
MY_TOKEN = config_registry.get_value("MY_TOKEN", "", label="My Token", ...)
def _update_token(value):
    global MY_TOKEN
    MY_TOKEN = value
config_registry.add_listener("MY_TOKEN", _update_token)

Convert to:

# New
MY_TOKEN = config_registry.get_var("MY_TOKEN", "", label="My Token", ...)

That’s it! Remove the listener function and add_listener call.

Registering Exposed Variables

When developing components (interfaces, plugins, or engines), you may need to expose configuration variables in the Web UI for users to configure. This is done by registering variables using the exposed variables system.

When to Register Variables

Register variables when: - Users need to configure your component through the Web UI - The variable should persist across restarts - The variable needs validation or specific UI controls

How to Register Exposed Variables

Import the registration function and call it at module level:

from core.variables_engine import register_exposed_var

# Register your variable (do this at module import time)
MY_TOKEN = register_exposed_var(
    "MY_TOKEN",  # Unique key (used in DB and API)
    label="My Service Token",  # Display name in Web UI
    default="",  # Default value
    description="Authentication token for connecting to My Service",  # Help text
    value_type=str,  # Python type: str, int, bool, list, dict
    sensitive=True,  # Hide value in UI (for passwords/tokens)
    advanced=False,  # Show in advanced settings only
    needs_component_reload=False,  # Restart component after change
)

Available Parameters

  • key: Unique identifier (uppercase with underscores)

  • label: Human-readable name for the UI

  • default: Default value (must match value_type)

  • description: Help text shown in the UI

  • value_type: Python type (str, int, bool, list, dict)

  • sensitive: Hide the value in logs and UI

  • advanced: Show only in advanced settings

  • needs_component_reload: Require component restart after changes

  • readonly: Prevent user editing

  • dangerous: Mark as potentially harmful

  • validator: Function or dict for input validation

  • tags: List of tags for organization

Using Registered Variables

After registration, access the variable through the config registry:

from core.config_manager import config_registry

# Get the current value
token = config_registry.get_var("MY_TOKEN", "")

# Use in your code
if token:
    client = MyServiceClient(token=str(token))

Migration Script

When deploying new exposed variables, run the pre-operation migration script to ensure database rows exist:

python core/scripts/preop_migrate_exposed_vars.py

This script creates database entries for any missing registered variables using their default values.

Best Practices

  • Register variables at module import time (not inside functions)

  • Use descriptive labels and descriptions

  • Mark sensitive data (tokens, passwords) as sensitive=True

  • Use appropriate value_type for validation

  • Test your component with the Web UI after registration

Need Help?

  • Check existing interfaces: interface/telegram_bot.py, interface/discord_interface.py

  • Check core modules: core/persona_manager.py

  • Ask in the development channel

Summary

DO: - Use get_var() for module-level configuration variables - Use ConfigVar objects naturally (they support bool, str, or, eq) - Create helper functions for complex value processing - Register exposed variables for user-configurable settings

DON’T: - Use get_value() + manual listeners for global variables - Update globals manually in listener functions - Assume values stay constant (they update automatically)

Remember: If you declare a configuration variable at module level, use get_var(). The system handles everything else automatically!