Skip to main content

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 generation
  • port: HTTP port for the Fiber app
  • body_limit: maximum request body size, parsed as a human-readable byte size; defaults to 32mib when omitted
  • trusted_proxies: proxy IPs/CIDRs whose forwarding headers Fiber may trust; empty means X-Forwarded-For is ignored

vef.api

Default per-operation rate limit, applied to every operation that does not declare its own OperationSpec.RateLimit:

[vef.api.rate_limit]
max = 100 # default
period = "5m" # default

The limiter counts requests per operation × client (IP + principal) in a sliding window held in process memory, so in a multi-node deployment each node enforces the limit independently.

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 (the config.DBKind enum, drivers registered in the framework runtime):

  • postgres
  • mysql
  • sqlite
  • oracle
  • sqlserver

Passing an unsupported kind at startup fails with an "unsupported database type" error.

For SQLite, path is optional. When omitted, the framework uses a shared in-memory database.

Network dialects also accept ssl_mode (disable | require | verify-ca | verify-full, default disable) and ssl_root_cert; dialect notes for SQL Server and Oracle live in Datasources.

vef.cors

CORS middleware settings:

[vef.cors]
enabled = true
allow_origins = ["http://localhost:3000", "https://my-app.com"]

Key fields:

  • enabled: enable CORS middleware
  • allow_origins: list of allowed origins

vef.event

Event bus, transport, routing, and consume middleware settings. A typical minimal setup enables the outbox transport and routes storage events to it:

[vef.event.transports.outbox]
enabled = true

[[vef.event.routing]]
pattern = "vef.storage.*"
transports = ["outbox", "memory"]

Key fields:

  • default_transport: fallback transport when no routing rule matches; defaults to "memory"
  • async_queue_size: capacity of the async fan-in queue; defaults to 4096
  • async_workers: goroutines draining the async queue; defaults to 4
  • publish_timeout: cap on an individual transport publish call; defaults to 5s

Per-transport blocks:

  • vef.event.transports.memory: in-process transport (queue_size, full_policy, publish_timeout)
  • vef.event.transports.tx_memory: in-process transactional transport; disabled by default (enabled = false)
  • vef.event.transports.outbox: persistent outbox (enabled, relay_interval, max_retries, batch_size, sink, cleanup_interval, completed_ttl)
  • vef.event.transports.redis_stream: Redis Streams transport; disabled by default (enabled = false)

Middleware and inbox toggles:

  • vef.event.middleware: logging, tracing, tracing_strict, metrics, recover, inbox
  • vef.event.inbox: retention, processing_lease, cleanup_interval

Routing rules are matched top-to-bottom; the first rule whose pattern matches wins, and fan-out is expressed by listing multiple transports.

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:

  • secret is 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_expires controls refresh-token lifetime and defaults to 168h
  • refresh_not_before defaults to 15m, half of the fixed access-token window
  • login and refresh rate limits default to 6 and 1 when unset or non-positive

vef.storage

Object storage settings:

[vef.storage]
provider = "filesystem"

[vef.storage.filesystem]
root = "./data/files"

Supported providers:

  • memory
  • filesystem
  • minio

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 startup
  • timeout_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.

vef.cron, vef.integration, and vef.push

Three optional feature sections, each off by default:

[vef.cron.store]
enabled = true # durable schedule store
auto_migrate = true

[vef.integration]
auto_migrate = true # read by vef.IntegrationModule
secret_key = "base64-key"

[vef.push]
enabled = true # WebSocket push endpoint at /ws

Full key lists live in the Configuration Reference and the module guides: Durable Schedules, Integration Engine, and Server Push.

Environment variables

Configuration keys are not overridable through environment variables — the generic prefix + dot-to-underscore override mechanism was removed. The framework reads exactly three standalone environment variables, none of which maps to a config key:

  • VEF_CONFIG_PATH — extra config search directory
  • VEF_LOG_LEVEL — log verbosity
  • VEF_I18N_LANGUAGE — framework 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.