Быстрый старт
dagstack/config — универсальный иерархический конфигурационный стек для приложений любого домена (web-сервисы, data-pipeline, workflow-оркестраторы, AI-платформы). Он обеспечивает:
- YAML как единый формат передачи — один и тот же файл читается одинаково в Python, TypeScript и Go.
- Слои конфигурации —
app-config.yaml(base) →app-config.local.yaml(переопределения разработчика) →app-config.${DAGSTACK_ENV}.yaml(переопределения окружения). - Подстановку env-переменных —
${VAR}/${VAR:-default}интерполируется при загрузке. - Типизированный доступ —
get_section/getSection/GetSection(зависит от языка) через pydantic / zod / struct-tags. - Маскирование секретов — поля с именами типа
api_key,*_token,*_passwordавтоматически не попадают в диагностические логи. - Hot-reload — подписка
config.onSectionChange()без перезапуска процесса.
:::info Статус публикации Пакеты Python и TypeScript готовятся к публикации на PyPI и npmjs.org. Пока доступны из внутреннего репозитория. Go-реализация — в дорожной карте; примеры ниже показывают планируемую сигнатуру. :::
Установка
- Python
- TypeScript
- Go
pip install dagstack-config
npm install @dagstack/config
go get go.dagstack.dev/config
Первая конфигурация
Создайте базовый файл app-config.yaml в корне приложения. Env-подстановки ищут переменные окружения при загрузке; без :-default отсутствующая переменная — ошибка.
app:
name: "order-service"
tagline: "Обработчик заказов"
database:
host: "${DB_HOST:-localhost}"
port: "${DB_PORT:-5432}"
name: "${DB_NAME:-orders}"
user: "${DB_USER}"
password: "${DB_PASSWORD}"
pool_size: 20
cache:
url: "${REDIS_URL:-redis://localhost:6379/0}"
ttl_min: 15
api:
host: "0.0.0.0"
port: 8080
request_timeout_s: 30
Локальные переопределения — в app-config.local.yaml (в .gitignore):
database:
pool_size: 5 # меньше соединений локально
api:
request_timeout_s: 120 # медленный отладчик
Специфичные для окружения — в app-config.production.yaml (коммитится):
database:
host: "prod-db.internal.example.com"
pool_size: 100
cache:
url: "redis://prod-cache.internal.example.com:6379/0"
При DAGSTACK_ENV=production реестр загрузит три файла в порядке base → local → production, выполнит deep-merge и вернёт итоговый Config.
Загрузка и чтение
- Python
- TypeScript
- Go
from dagstack.config import Config
config = Config.load("app-config.yaml")
# Базовые методы доступа:
print(config.get_string("app.name")) # "order-service"
print(config.get_int("database.pool_size")) # 20
print(config.get_int("api.port")) # 8080
# Со значением по умолчанию — если путь отсутствует, вернётся указанное значение:
print(config.get_int("api.max_body_mb", default=10)) # 10
import { Config } from "@dagstack/config";
const config = await Config.load("app-config.yaml");
console.log(config.getString("app.name")); // "order-service"
console.log(config.getInt("database.pool_size")); // 20
console.log(config.getInt("api.port")); // 8080
console.log(config.getInt("api.max_body_mb", 10)); // 10
import (
"context"
"go.dagstack.dev/config"
)
cfg, err := config.Load(context.Background(), "app-config.yaml")
if err != nil {
return err
}
name, _ := cfg.GetString("app.name") // "order-service"
pool, _ := cfg.GetInt("database.pool_size") // 20
port, _ := cfg.GetInt("api.port") // 8080
maxBody, _ := cfg.GetIntDefault("api.max_body_mb", 10) // 10
Типизированный доступ
Вместо серии вызовов get_string / get_int (Python) / getString / getInt (TS) / GetString / GetInt (Go) — объявите секцию как модель и получите её целиком с валидацией.
- Python
- TypeScript
- Go
from pydantic import BaseModel, Field
from dagstack.config import Config
class DatabaseConfig(BaseModel):
host: str
port: int = Field(5432, ge=1, le=65535)
name: str
user: str
password: str = Field(..., min_length=1)
pool_size: int = Field(20, ge=1, le=1000)
config = Config.load("app-config.yaml")
db = config.get_section("database", DatabaseConfig)
# Доступ через атрибуты, с валидацией:
pool = create_pool(host=db.host, port=db.port, pool_size=db.pool_size)
import { z } from "zod";
import { Config } from "@dagstack/config";
const DatabaseConfig = z.object({
host: z.string(),
port: z.number().int().min(1).max(65535).default(5432),
name: z.string(),
user: z.string(),
password: z.string().min(1),
pool_size: z.number().int().min(1).max(1000).default(20),
});
const config = await Config.load("app-config.yaml");
const db = config.getSection("database", DatabaseConfig);
const pool = createPool({ host: db.host, port: db.port, poolSize: db.pool_size });
type DatabaseConfig struct {
Host string `yaml:"host"`
Port int `yaml:"port" validate:"min=1,max=65535"`
Name string `yaml:"name"`
User string `yaml:"user"`
Password string `yaml:"password" validate:"required"`
PoolSize int `yaml:"pool_size" validate:"min=1,max=1000"`
}
cfg, _ := config.Load(context.Background(), "app-config.yaml")
var db DatabaseConfig
if err := cfg.GetSection("database", &db); err != nil {
return err
}
pool := createPool(db.Host, db.Port, db.PoolSize)
Валидация schema выполняется сразу в getSection; ошибка — ConfigError(validation_failed) с указанием поля.
Перезагрузка конфига в рантайме
Если источник конфигурации поддерживает watch (обычно YamlFileSource через fsnotify), компоненты могут подписаться на изменения своей секции:
- Python
- TypeScript
- Go
sub = config.onSectionChange("database", DatabaseConfig, callback=lambda new: apply_pool(new))
# ... работа ...
sub.unsubscribe()
const sub = config.onSectionChange("database", DatabaseConfig, (newConfig) => applyPool(newConfig));
// ...
sub.unsubscribe();
sub, _ := cfg.OnSectionChange("database", &DatabaseConfig{}, func(newCfg any) { applyPool(newCfg.(*DatabaseConfig)) })
defer sub.Unsubscribe()
Если источник не умеет отслеживать изменения — подписка принимается, но active = false с inactive_reason = "subscription_without_watch". Код компонента не ломается, просто callback не вызывается.
Для каких приложений подходит
dagstack/config не зависит от домена приложения. Подходит одинаково для:
- Web / API-сервисов — секции
database,cache,api,auth,rate_limit,workers. - Data-pipeline — секции
source,processor,sink,scheduler,retry_policy. - Workflow-оркестраторов — секции
queue,executor,storage,retry. - AI / RAG-платформ — секции
llm,embedder,vector_store,retrieval. - Notification-систем — секции
email,sms,push,webhook. - Биллинг / платёжных сервисов — секции
payment_provider,tax,subscriptions.
Механика одинакова: вы объявляете секцию в YAML, описываете schema в нативном для языка формате, читаете через getSection(). Знания о домене живут в приложении, не в конфиг-стеке.
Что дальше
Понятия — как устроен конфиг-стек:
Руководства — как решать типовые задачи:
Справочник — точные таблицы:
Спецификация — нормативные решения:
API (генерация отложена до стабилизации реализаций):
- Python — готовится через
pydoc-markdown.