You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: packages/varlock-website/src/content/docs/guides/dynamic-config.mdx
+29-3Lines changed: 29 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -82,13 +82,39 @@ See framework-specific recipes:
82
82
83
83
## Prerender/build guardrails
84
84
85
-
Dynamic config should not be consumed in static prerender/build contexts unless explicitly designed for it.
85
+
Baking a public+dynamic value into prerendered output defeats its purpose: you marked it `@dynamic` because the build-time value should not be frozen. Varlock catches this per framework:
86
86
87
-
Varlock runtime can detect this and warn/error when dynamic keys are accessed during prerender/build phases, helping catch accidental usage early.
87
+
-**Next.js**: accessing a public+dynamic key during server rendering marks the route dynamic (it will not be statically prerendered), including access from nested components.
88
+
-**Vite-based frameworks** (Astro, SvelteKit, etc.): accessing a public+dynamic key while a build/prerender is running throws an error. Set `_VARLOCK_DYNAMIC_BUILD_ACCESS_MODE=warn` to downgrade it to a warning (e.g. while migrating an existing app).
89
+
90
+
Sensitive values are not subject to this guard. Reading a sensitive value server-side during a static build (e.g. using an API key to fetch data while generating pages) is fine; leak detection separately errors if the value itself ends up in the built output.
91
+
92
+
## Filtering by static/dynamic
93
+
94
+
The [`--filter` selector language](/reference/cli-commands/#filtering-items) supports a `@dynamic` selector (negate it for static items), so you can scope a load or a generated file to one side of the split:
95
+
96
+
```bash
97
+
varlock load --filter="!@dynamic"# only build-time-inlineable items
98
+
varlock load --filter="@dynamic"# only runtime-resolved items
99
+
varlock run --filter="@dynamic" -- node app # inject only runtime values
100
+
```
101
+
102
+
```env-spec title=".env.schema"
103
+
# generate a module covering only runtime public values
Like all filters, `@dynamic` filters scope **resolution and validation**, not just output: varlock resolves each item's decorator metadata first (cheap), then only resolves and validates the items the filter selects. So a build-time load can skip runtime-only vars entirely, including their `@required` checks and any value resolvers they use:
108
+
109
+
```bash
110
+
# runtime-only vars (e.g. platform-injected at runtime) don't exist yet at build
111
+
# time - exclude them so their @required checks don't fail the build
112
+
varlock load --filter='!@dynamic'
113
+
```
88
114
89
115
## Guidance
90
116
91
-
-Start with `@defaultDynamic=inferFromSensitive` for predictable defaults.
117
+
-The default (`@defaultDynamic=inferFromSensitive`) keeps today's behavior: sensitive stays out of bundles, public gets inlined. You only need decorators when you want something different.
92
118
- Mark intentional runtime public values with `@dynamic`.
93
119
- Keep dynamic+public loading scoped to only the parts of your app that need it.
94
120
- Prefer one app-level endpoint/payload shape per app unless you have a strong reason to split.
Copy file name to clipboardExpand all lines: packages/varlock-website/src/content/docs/reference/cli-commands.mdx
+6-2Lines changed: 6 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -62,7 +62,7 @@ Several commands share these flags. They behave identically wherever they appear
62
62
63
63
- a key name or glob, e.g. `STRIPE_*` (matches `*` and `?`)
64
64
-`!selector` to negate any of the below, e.g. `!STRIPE_DEBUG_KEY`
65
-
-`@sensitive` / `@required` to select by decorator
65
+
-`@sensitive` / `@required`/ `@dynamic`to select by decorator (negate for the opposite, e.g. `!@dynamic` selects static items)
66
66
-`#tagname` to select items tagged via [`@tag(tagname)`](/reference/item-decorators/#tag)
67
67
68
68
**How selectors combine:** every non-negated selector is OR'd together into one inclusion set, regardless of kind: mixing a glob, a `@decorator`, and a `#tag` in the same filter just widens that set. Anything matching a negated (`!`) selector is then subtracted from that set, again regardless of kind. If a filter has no non-negated selectors at all, the inclusion set starts as "everything" before negations are subtracted.
@@ -87,7 +89,9 @@ Can also be set via the [`_VARLOCK_FILTER`](/reference/reserved-variables/#_varl
87
89
88
90
A filter that matches no items (e.g. a typo'd key or tag) prints a warning to stderr. The command still succeeds, with empty output on `load` or no schema vars injected on `run`.
89
91
90
-
**A key name/glob/tag-only filter also scopes resolution and validation**, not just output: only items it selects (plus their dependencies) are resolved, so an unrelated broken item outside the filter won't block `load`/`run`. This is useful for scoping validation differently across contexts, e.g. a build step that only needs `--filter="#frontend"` shouldn't fail because an unrelated backend-only var is misconfigured. `@sensitive`/`@required` selectors can't be scoped this way (which items match isn't knowable until the graph is already resolved), so a filter using either falls back to resolving and validating everything, same as no `--filter` at all.
92
+
**A `--filter` also scopes resolution and validation**, not just output: only items it selects (plus their dependencies) are resolved, so an unrelated broken item outside the filter won't block `load`/`run`, and excluded items' value resolvers (exec commands, secrets managers, etc.) never run. This is useful for scoping validation differently across contexts, e.g. a build step that only needs `--filter="#frontend"` shouldn't fail because an unrelated backend-only var is misconfigured, and `--filter="!@dynamic"` at build time skips runtime-only vars (e.g. platform-injected values that don't exist yet at build time) including their `@required` checks.
93
+
94
+
Decorator selectors match on *computed* state, which can be value-dependent (e.g. `@required=forEnv(prod)`), so for those varlock resolves each candidate item's decorator metadata first (cheap - no value resolvers run), then matches exactly and only resolves values for selected items. Values that decorator functions themselves reference (e.g. `@required=eq($OTHER, x)` needs `OTHER`) are true dependencies of evaluating the filter and do get resolved.
Copy file name to clipboardExpand all lines: packages/varlock-website/src/content/docs/reference/item-decorators.mdx
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -158,6 +158,8 @@ Sets whether the item is _dynamic_ - meaning integrations should avoid replacing
158
158
159
159
By default, dynamic behavior follows sensitivity (sensitive items are dynamic, non-sensitive items are static), but this can be overridden globally with [`@defaultDynamic`](/reference/root-decorators/#defaultdynamic).
160
160
161
+
Items can also be selected by this state via the `@dynamic` selector (or `!@dynamic` for static items) in the [`--filter` language](/reference/cli-commands/#filtering-items). See the [Static vs Dynamic Config guide](/guides/dynamic-config/) for the full model.
Set to `warn` to downgrade the build/prerender-time guard on [public+dynamic](/guides/dynamic-config/) config access from an error to a one-time warning per key. Useful while migrating an existing app that still reads dynamic public values in prerendered pages.
50
+
47
51
### `_VARLOCK_THROW_ON_LOAD_ERROR`
48
52
49
53
When set (`1` / `true`), [`varlock/auto-load`](/integrations/javascript/#reporting-load-failures) throws the error on a load failure instead of exiting, so an already-initialized error tracker (e.g. Sentry) can capture it via its `uncaughtException` handler. Setting a `globalThis._varlockOnLoadError` hook enables the same throw behavior. See [Reporting load failures](/integrations/javascript/#reporting-load-failures).
@@ -67,3 +71,7 @@ The serialized env graph (resolved config values plus metadata) injected by [`va
67
71
### `__VARLOCK_RUN`
68
72
69
73
A marker set so a child process can detect that it is running under `varlock run`.
74
+
75
+
### `__VARLOCK_EXECUTION_PHASE`
76
+
77
+
Set to `build` by build-time integrations (e.g. the Vite plugin during `vite build`) so the runtime can detect app code executing during build/prerender and apply the [public+dynamic access guard](/guides/dynamic-config/#prerenderbuild-guardrails).
Copy file name to clipboardExpand all lines: packages/varlock-website/src/content/docs/reference/root-decorators.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -537,7 +537,7 @@ Every `@generate*` decorator below is a root decorator that _can be called multi
537
537
-`path`: Relative filepath to write the generated file to.
538
538
-`auto`: Controls whether generation runs automatically on every load (defaults to `true`). Set to `false` to generate only when you run [`varlock codegen`](/reference/cli-commands/#codegen) explicitly, useful in a CI pipeline or a dedicated build step.
539
539
-`executeWhenImported`: overrides the default of not executing when the containing file is imported (defaults to `false`).
540
-
-`filter`: Restrict this generated file to a subset of items, using the same selector language as the CLI [`--filter` flag](/reference/cli-commands/#filtering-items): key names/globs, `!negations`, `@sensitive`/`@required`, and `#tagname` (set via [`@tag()`](/reference/item-decorators/#tag)). Quote the value if it has more than one comma-separated selector (the decorator parser splits args on unquoted commas), e.g. `filter="STRIPE_*,!STRIPE_DEBUG_KEY"`. Call the same decorator multiple times with different `path`/`filter` pairs to emit several subset files from one schema.
540
+
-`filter`: Restrict this generated file to a subset of items, using the same selector language as the CLI [`--filter` flag](/reference/cli-commands/#filtering-items): key names/globs, `!negations`, `@sensitive`/`@required`/`@dynamic`, and `#tagname` (set via [`@tag()`](/reference/item-decorators/#tag)). Quote the value if it has more than one comma-separated selector (the decorator parser splits args on unquoted commas), e.g. `filter="STRIPE_*,!STRIPE_DEBUG_KEY"`. Call the same decorator multiple times with different `path`/`filter` pairs to emit several subset files from one schema.
541
541
542
542
```env-spec
543
543
# only ship billing-tagged keys to the billing package's generated types
0 commit comments