Recipes for a fuz_app server's .env.production content.
Every fuz_app server shares the same .env.production preamble — the
NODE_ENV/PORT/HOST header, the Unix-socket DATABASE_URL, the
cookie-signing key, and the bootstrap-token path — plus, behind a reverse
proxy, the load-bearing trusted-proxies comment block. These build that shared
content once so a config concatenates it with its app-specific vars instead of
re-typing security-relevant prose that must converge:
- fuz_app_env_base — the common header through
FUZ_BOOTSTRAP_TOKEN_PATH. - fuz_app_trusted_proxies — the
FUZ_<infix>_TRUSTED_PROXIES stanza (the
comment explaining why a missing value collapses per-IP rate limits and
ruins audit_log.ip), keyed by the service's env-var infix.
Templating boundary
fuz_app_env_base returns the whole zap template content,
{type: 'template', template, vars, kinds}: the caller passes the values
(port, cookie_keys) and its own lines (extra), and gives the result as the
file's content. The header
holds {{port}} / {{cookie_keys}} as placeholders, substituted at apply time,
while eval-time values (user, service, config_dir) are interpolated here. A
caller's lines (allowed origins, facts dirs, SMTP, …) bring their own variables and
kinds; fuz_app_trusted_proxies returns text for them.
Quoting
The file has two readers: systemd reads it as an EnvironmentFile=, and OpenRC's
init script reads it as data by a subset of those rules (see
recipe_alpine_openrc.ts). So each value these interpolate or leave for the
template engine is one single-quoted word (single_quote), which both read
literally, \ included. systemd honours a quote only where it opens the value, so
after the closing ' it reads quotes as text and \ as an escape ('\''
misreads), and it ends a value at a line break; the OpenRC reader refuses such a
line. A value holding a ', a line break, or a NUL is refused at eval
(ENV_FILE_VALUE), naming the field.
A template value substituted into '{{cookie_keys}}' is the template engine's
text, not quoted again, since it resolves at apply time: a value holding a '
would end the quoting, so systemd would read a different value and OpenRC would
refuse to start the service. So cookie_keys is an env_value, which zap refuses
a ' (and a line break), and {{port}} a port, which holds no quote. The lines
a caller appends are the caller's to quote, by the format recipe_alpine_openrc.ts
documents, and the placeholders in them the caller's to give a kind.
Recipes are imported at config eval time inside zap's sandboxed config eval
(fgro eval), which has no network, no writes, and no environment, and where
the clock and randomness throw. This module is therefore a set of pure functions: each only
assembles a string from its arguments — no I/O, no clock, no randomness, no
imports beyond types and the pure single_quote. The output is deterministic for
given inputs (stable ordering, trailing newline), so it stays a reliable
content-addressed plan artifact.