Configuration
VEF reads configuration from application.toml through the config module, then injects strongly typed config structs into the rest of the runtime.
File lookup order
At startup, the framework config loader searches for application.toml in:
./configs$VEF_CONFIG_PATH.../configs
If the file cannot be read, startup fails immediately.
Core sections
These sections map directly to the public config package and internal module constructors. For the complete config public surface, including exported structs, fields, and methods, see Configuration Reference.
vef.app
Application-level settings:
[vef.app]
name = "my-app"
port = 8080
body_limit = "32mib"
Key fields:
name: used as the app name and as input to JWT audience generationport: HTTP port for the Fiber appbody_limit: parsed by Fiber; defaults to32mibwhen omitted
vef.api
Default per-operation rate limit (v0.38), applied to every operation that does
not declare its own OperationSpec.RateLimit:
[vef.api.rate_limit]
max = 100 # default
period = "5m" # default
vef.data_sources
Database connection settings:
[vef.data_sources.primary]
type = "postgres"
host = "127.0.0.1"
port = 5432
user = "postgres"
password = "postgres"
database = "my_app"
schema = "public"
enable_sql_guard = true
The primary entry is mandatory and powers the framework-wide orm.DB
injection. Additional named data sources use the same shape:
[vef.data_sources.analytics]
type = "sqlite"
path = "./analytics.db"
Supported type values (drivers registered in the framework runtime):
postgresmysqlsqlite
For SQLite, path is optional. When omitted, the framework uses a shared in-memory database.
The
config.DBKindenum also declaresoracleandsqlserverconstants for future use, but the framework does not currently ship runtime providers for them — configuring those values returnsdatabase.ErrUnsupportedDBKindat boot.
vef.cors
CORS middleware settings:
[vef.cors]
enabled = true
allow_origins = ["http://localhost:3000", "https://my-app.com"]
Key fields:
enabled: enable CORS middlewareallow_origins: list of allowed origins
vef.security
Security-related runtime settings:
[vef.security]
secret = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
token_expires = "168h"
refresh_not_before = "15m"
login_rate_limit = 6
refresh_rate_limit = 1
Runtime notes:
secretis the hex-encoded JWT signing key. Leave it empty only for local development; the framework then generates an ephemeral per-process key, so tokens do not survive restart or work across nodes. Generate and set a stable private value for production.- access tokens issued by the built-in JWT token generator expire after
30m token_expirescontrols refresh-token lifetime and defaults to168hrefresh_not_beforedefaults to15m, half of the fixed access-token window- login and refresh rate limits default to
6and1when unset or non-positive
vef.storage
Object storage settings:
[vef.storage]
provider = "filesystem"
[vef.storage.filesystem]
root = "./data/files"
Supported providers:
memoryfilesystemminio
If provider is omitted, VEF uses memory storage.
For non-test deployments, set filesystem or minio; in-memory objects are lost on restart.
The filesystem provider defaults root to ./storage, and MinIO defaults its bucket to vef.app.name or vef-app when minio.bucket is empty.
vef.redis
The default boot graph includes the Redis module during vef.Run(...).
Redis is opt-in. When vef.redis.enabled is omitted or false, the framework injects a nil *redis.Client and skips startup PING; Redis-backed modules that depend on Redis must either stay dormant or require you to enable Redis explicitly.
When enabled = true and connection settings are omitted, the client defaults to:
- host:
127.0.0.1 - port:
6379 - network:
tcp
So in minimal examples, leave vef.redis out unless the application really depends on Redis. When it does, configure enabled = true intentionally.
vef.monitor
Monitoring configuration is injected into the monitor module. The module also applies its own defaults internally.
The default sampling interval is 10s, with a 2s sampling window.
vef.mcp
MCP support is present in the runtime, but the MCP server only activates when enabled in configuration.
The /mcp endpoint requires Bearer auth by default. If vef.mcp.require_auth is omitted or set to true, unauthenticated requests are rejected; set it to false only for deliberately anonymous MCP surfaces.
vef.approval
Approval workflow engine settings:
[vef.approval]
auto_migrate = true
timeout_scan_interval = "1m"
pre_warning_scan_interval = "5m"
cleanup_scan_interval = "24h"
delegation_max_depth = 10
form_snapshot_retention = "2160h" # 90 days
urge_record_retention = "720h" # 30 days
cc_record_retention = "2160h" # 90 days
Key fields:
auto_migrate: run the approval DDL migration on startuptimeout_scan_interval: cadence of the timeout scanner (default: 1m)pre_warning_scan_interval: cadence of the pre-warning scanner (default: 5m)cleanup_scan_interval: cadence of the retention cleanup job (default: 24h)delegation_max_depth: maximum delegation chain depth (default: 10)form_snapshot_retention/urge_record_retention/cc_record_retention: retention windows for the corresponding tables
config.ApprovalConfig.ApplyDefaults() fills the timing and retention defaults above but does not enable AutoMigrate; migrations run only when auto_migrate = true.
The outbox-related fields previously lived under
[vef.approval](outbox_relay_interval,outbox_max_retries,outbox_batch_size). They have moved to[vef.event.transports.outbox]so the framework-wide outbox transport can serve any module — see Event Bus.
Environment overrides
VEF uses an environment prefix and dot-to-underscore replacement, so config keys can be overridden with environment variables.
Examples:
VEF_CONFIG_PATHVEF_LOG_LEVELVEF_NODE_IDVEF_I18N_LANGUAGE
What configuration does not do
Configuration does not replace application composition. You still use code to:
- register resources
- provide services and modules
- register auth loaders and permission resolvers
- register CQRS behaviors
- register MCP providers
Think of configuration as runtime input, not application structure.
Next step
Once the config file is clear, move to Project Structure to organize a real project around modules.