Upgrade Notes to v0.35
This page is the cross-version audit map for the backend commits from
9e7e009 feat: added multi-data source functionality through v0.35.0
(9e7e009^..v0.35.0). Use it when upgrading an application whose docs or
integration assumptions were last checked before the multi-data-source work.
It is not a replacement for the generated indexes. After applying the migration notes below, verify exact Go symbols and wire fields against the Public API Index and Runtime API Index.
Immediate Checklist
- Replace any old single-source database config with
[vef.data_sources.primary]. Theprimaryentry is mandatory and reserved; additional sources live under[vef.data_sources.<name>]. - Update renamed Go symbols: the old env key prefix constant is now
config.EnvPrefix, the old CORS config type is nowconfig.CORSConfig,datasource.Spec.Cfgis nowdatasource.Spec.Config, and the SSL constants areSSLDisable,SSLRequire,SSLVerifyCA, andSSLVerifyFull. - If a client signs API requests, update the canonical payload to include the
HTTP method and path:
app_id=<appID>&method=<method>&nonce=<nonce>&path=<path>×tamp=<timestamp>. The request body is intentionally not part of the HMAC payload. - Treat
/mcpas Bearer-protected by default. Setvef.mcp.require_auth=falseonly when the MCP surface is deliberately anonymous. - Enable approval explicitly with
vef.ApprovalModule; it is no longer part of the default boot graph. Approval events must route through a transactional transport and any framework subscriber also needs a subscribable sink. - Review approval designer/runtime contracts: node config is normalized and
validated at deploy time,
PassRuleis limited toall,any, andratio, instance detail now includesformSchema,timeline, andflowGraph, and business binding uses opaquebusinessRefplus engine-owned status write-back. - Use the storage multipart upload protocol (
init_upload,upload_part,list_parts,complete_upload,abort_upload) and the/storage/files/<key>proxy. Public uploads requirevef.storage.allow_public_uploads=true. - For
vef-cli generate-model-schema, regenerate schemas when models use Bun table aliases, bare table tags, default table names, orm2mrelations; the generator now follows Bun tag parsing more closely.
Release-by-Release Audit
| Release | User-facing changes to review |
|---|---|
v0.27.0 | Multi-data-source config and registry, mandatory primary, datasource.Provider, Registry.Register / Update / Unregister / Reconcile / TestConnection / HealthCheck, and the removal of the legacy vef.data_source fallback. |
v0.28.0 | Core expression engine, source-IP security checks, MCP read-only query guard, event middleware ordering and tracing, storage and CRUD API error surfaces, approval opt-in module and route checks, and stricter request error handling. |
v0.29.0 / v0.29.1 | Effective*() config defaults, database TLS support, dialect-aware SQL guard hardening, approval tenant fail-closed behavior, signature replay-window fixes, and security.UserMenu JSON alignment. |
v0.30.0 | Restored sequence DB/Redis stores, cache.NewPrefixKeyBuilderWithSeparator, logx.LoggerConfigurable, and reflectx.BreadthFirst. |
v0.31.0 | The expression backend moved to pure-Go expr-lang; expression itself no longer requires CGO_ENABLED=1. |
v0.32.x | Config API renames (EnvPrefix, CORSConfig, SSL constants), restored approval module after hardening, and Bun-compatible model-schema generation fixes. |
v0.33.x | Approval detail DTOs gained formSchema, timeline, flow graph progress, action-log metadata, and persistent flow-node ids for rollback targeting; model-schema parsing became a faithful Bun tag parser. |
v0.34.0 | Custom auth strategies via vef.ProvideAuthStrategy and source-IP whitelist auth via api.IPAuth(...), vef.security.ip_whitelists, and vef.app.trusted_proxies. |
v0.35.0 | Approval instance detail moved to the engine-recorded visit trail with person snapshots; condition routing snapshots host globals; business binding uses opaque refs; approval domain events were rebuilt as self-describing envelopes. |
Data Sources and Database
Configuration now uses a named data-source map:
[vef.data_sources.primary]
type = "postgres"
host = "127.0.0.1"
port = 5432
user = "postgres"
password = "postgres"
database = "app"
schema = "public"
[vef.data_sources.analytics]
type = "sqlite"
path = "./analytics.db"
primary is both config.PrimaryDataSourceName and
datasource.PrimaryName. It powers the framework-wide orm.DB injection and
cannot be dynamically registered, updated, or unregistered. Runtime providers
currently exist for postgres, mysql, and sqlite; the oracle and
sqlserver DBKind constants are reserved for future providers.
The datasource.Registry is the public extension point for named runtime
sources. It exposes Primary, Get, Has, Names, Kind, Register,
Update, Unregister, Reconcile, TestConnection, and HealthCheck.
TestConnection opens a throwaway connection, returns
datasource.ConnectionInfo.Version, and does not mutate the registry.
Network database sources can opt into TLS with ssl_mode and ssl_root_cert.
Use disable, require, verify-ca, or verify-full. SQLite ignores these
fields.
API, Security, and MCP
The auth strategy registry is now extensible through
vef.ProvideAuthStrategy(...). The built-in strategies are none, bearer,
signature, and ip; api.IPAuth() uses the default whitelist and
api.IPAuth("ops") targets vef.security.ip_whitelists.ops.
Behind a proxy, configure vef.app.trusted_proxies; otherwise forwarded headers
from untrusted clients are ignored and IP-based auth sees the direct peer.
Request decoding and auth failures are now more explicit:
- malformed params and meta return
api.ErrInvalidRequestParams/api.ErrInvalidRequestMetawith HTTP 400 semantics; - invalid Bearer tokens return 401 rather than falling through as anonymous;
- public operations get an isolated anonymous principal.
MCP database queries are read-only and dialect-aware, and /mcp requires Bearer
auth unless vef.mcp.require_auth=false.
Event Bus
Applications should treat events as the current multi-transport platform:
memory, transactional outbox, Redis Streams, optional Inbox dedupe, deterministic
publish/consume middleware ordering, W3C trace propagation, and route
inspection through event.RouteInspector.
Routes that use the transactional outbox for framework subscribers must include
a subscribable sink such as redis_stream or memory; an ["outbox"]-only
route is valid only for publisher-only flows. When Inbox dedupe is enabled, its
retention window must outlast the outbox retry horizon so delayed duplicates are
still recognized.
Approval and storage both publish transactional events and fail fast at startup when required event routes are missing.
Approval
Approval is now an optional feature module. Add vef.ApprovalModule to
vef.Run(...) before relying on any approval/* resources, CQRS handlers,
binding listeners, timeout scanners, or approval event publication.
Important runtime and API changes:
- deploy validates and normalizes flow node config before publication;
- conditions use approval's own
expr-langevaluator, not the publicexpression.Engine; - host-resolved globals come from
approval.InstanceGlobalsResolverat instance start and are snapshotted ontoInstance.Globals; FlowGraphNode.Kindisapproval.NodeKind;- instance detail DTOs expose
formSchema,timeline, andflowGraph; FlowGraphNode.NodeIDis the persistent flow-node id used by action logs and rollback targets;- business binding uses opaque
Instance.BusinessRef; the engine owns the final status write-back and usesBusinessRefResolveronly to extract the record id; - domain events use
InstanceEventBase,TaskEventBase, andFlowEventBaseso subscribers can act from the event envelope without immediately loading approval tables; - tenant handling is fail-closed for empty or ambiguous caller context.
See Approval Module for the full resource, DTO, event, and extension-point contract.
Expression and Mold
expression.Engine is part of the core boot graph and is injectable into API
handlers. The current backend is pure-Go expr-lang; the older Goja-based js
package remains separate. Mold's expr transformer evaluates against the
containing struct in declaration order, so derived fields can reference earlier
sibling fields.
Storage
The storage resource is multipart-only at the HTTP layer. The business-side file
lifecycle uses storage.Files / storage.FilesFor[T], claim consumption, and
pending-delete rows so file ownership changes commit or roll back with the
business transaction. Delete failures that exhaust retries emit
vef.storage.delete.dead_letter and are removed from the queue.
Default download URLs resolve through /storage/files/<key> unless a host
application supplies a custom URLKeyMapper. Public keys under pub/ are
served anonymously; private keys call storage.FileACL.
Other Public Surfaces
- CRUD mutation responses use operation-specific success messages, and CRUD /
storage / approval outward errors use coded
result.Errorvalues. - Result error sentinels freeze their i18n text at initialization time; language switches affect later translations but not already-constructed sentinel values.
i18n.SetLanguage(...)is race-free with concurrentT/Tecalls.sequence.NewDBStore,sequence.NewRedisStore, and injectable*sequence.MemoryStoreare available for durable, distributed, and startup seeding scenarios.cache.Invalidating[T]is the event-invalidated read-through cache wrapper;cache.NewPrefixKeyBuilderWithSeparatorpreserves the separator exactly as provided.logx.LoggerConfigurable[T]andreflectx.BreadthFirstare part of the public utility surface again.- The monitor module deduplicates disk devices by unique partitions rather than merging independent devices.
- Schema inspection uses the primary data source.