Skip to main content

Plugin configuration

Plugins receive their settings through the unified dagstack/config-spec mechanism. The application configuration is a hierarchical YAML file with layers (app-config.yamlapp-config.local.yamlapp-config.${DAGSTACK_ENV}.yaml). Each plugin declares its own section and receives the contents inside setup.

Plugin section in the config

The section name is <kind>.<name>. For a stripe plugin of kind payment_provider the section is payment_provider.stripe:

app-config.yaml
payment_provider:
stripe:
base_url: "${STRIPE_BASE_URL:-https://api.stripe.com/v1}"
api_key: "${STRIPE_API_KEY}"
webhook_secret: "${STRIPE_WEBHOOK_SECRET}"
timeout_s: 30
max_retries: 3

The substitutions ${VAR:-default} and ${VAR} are applied automatically when the configuration is loaded.

Schema declaration

A plugin declares a pydantic schema for its section. The registry runs the schema for validation before passing the config into setup. An invalid configuration raises ManifestInvalid (or its equivalent in other languages) with the offending field highlighted.

plugins/payment_provider/stripe/plugin.py
from pydantic import BaseModel, Field
from dagstack.plugin_system import PluginContext


class StripeConfig(BaseModel):
base_url: str = "https://api.stripe.com/v1"
api_key: str = Field(..., min_length=1)
webhook_secret: str = Field(..., min_length=1)
timeout_s: float = 30.0
max_retries: int = 3


class StripeProvider:
config_schema = StripeConfig

async def setup(self, context: PluginContext) -> None:
config = StripeConfig(**context.config) # already validated
self._client = build_client(
base_url=config.base_url,
api_key=config.api_key,
timeout=config.timeout_s,
)

Sensitive fields and secrets

Fields that hold secrets (API keys, passwords, tokens) must be masked in logs. dagstack/config-spec keeps a list of field names that are treated as secrets by default:

  • api_key, secret_key, access_token, password, client_secret
  • any field whose name ends with _secret, _token, _password, _key

When the configuration is printed (for example, in startup diagnostics) the values of these fields are replaced with [MASKED]. This does not absolve the plugin of responsibility — do not log config.api_key directly.

Runtime configuration updates

:::info Phase 2 scope Subscriptions to live config-section updates (on_section_change / Subscription) are part of the Phase 2 dagstack/config-spec integration and are not exposed in 0.1.0-rc.2. Today plugins receive their config once during setup; reapplying changes requires a teardown_all() / setup_all(ctx) cycle. :::

Testing with an ad-hoc config

For unit tests pass the configuration explicitly, bypassing file-based loading:

import asyncio
import logging
from dagstack.plugin_system import PluginContext, PluginRegistry

registry = PluginRegistry()
context = PluginContext(
config={"api_key": "test-key"},
logger=logging.getLogger("test"),
registry=registry,
)
plugin = StripeProvider()
asyncio.run(plugin.setup(context))

See also

  • dagstack/config-spec — the full specification of hierarchical configuration, substitutions and source adapters.
  • Plugin lifecycle — where the configuration is applied during the setup/teardown cycle.