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.

Protected API body transport is optional. When enabled, JSON bodies on /api use the configured authenticated encoding:

[vef.api.body_encoding]
enabled = true
encoding = "aes-gcm+base64" # or "sm4-gcm+base64"
key = "<standard-base64-key>"

The key is standard base64 and must match the client. AES keys may be 16, 24, or 32 bytes; SM4 keys must be 16 bytes. This layer does not replace HTTPS.

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.