Summary#
app.Config declares the per-environment settings your application uses — non-secret values that differ between
development and production, such as an invite base URL (http://localhost:8099 locally, https://app.example.com in
production) or a from-address. It is the non-secret twin of declaring secrets (app.Secrets): you declare a setting's name in
source and supply its per-environment values out of band, but — unlike a secret — a setting's value is public and is
readable everywhere through the Config.Name handle, in a function body and in a component alike.
app.Config = [
new Setting("InviteBaseUrl") { Default = "http://localhost:8099" },
new Setting("FromAddress") { Default = "noreply@localhost" },
];
// read it anywhere by handle — it resolves to the running environment's value:
string invite = Config.InviteBaseUrl + "/accept?token=" + token;Signature#
app.Config = [ // one entry per named setting
new Setting("Name") { Default = "…" }, // Default: the value used when no .env supplies one
new Setting("Name"), // no Default: the value is REQUIRED from a .env file
];
Config.Name // the setting's value for the running environment (a string)app.Config is a list — an app may declare as many named settings as it needs.
Description#
A setting has a name (a string literal, the Config.Name read key) and an optional Default. The name and the
Default are the only things that live in source. The actual per-environment values come from two checked-in files that
sit next to your app.osy:
.env.developmentand.env.production— plainKEY=VALUEfiles (#comments and blank lines ignored). A key names a setting; its value is that setting's value for that environment.
These .env files are compile inputs, not runtime reads: when you compile, both value sets are baked into the app,
so one compiled app carries its development and its production values and needs no per-environment recompile. For each
setting, the value in each environment is the .env override if present, otherwise the setting's Default.
At runtime the app selects the set matching the environment it is running in — a local dev server resolves the
development values, a production deployment resolves the production ones — so Config.InviteBaseUrl reads
http://localhost:8099 locally and https://app.example.com in production, from the same build.
Reading a setting. Config.Name is a plain string value. Use it in a function body (building an email link,
choosing a from-address) and in a component (a link, a label). Reading a setting that is not declared in app.Config is
a compile error — a typo never silently reads as empty.
Required vs. defaulted. A setting with a Default always has a value. A setting without a Default is
required: if either .env.development or .env.production does not supply it, the compile fails — you cannot ship a
build that is missing a value in an environment it targets. An .env key that matches no declared setting is likewise a
compile error, so a stale or mistyped key is caught rather than silently ignored.
Not for secrets. app.Config values are stored and served in the clear — they are meant to be public (a URL, an
address), and a component can read them. An API key, token, or password belongs in declaring secrets (app.Secrets), which keeps its
value out of source and never sends it to a browser. A config value that looks like a secret (a long random token, an
sk-… key) is flagged with a warning nudging you toward app.Secrets.
In the Admin portal#
When you deploy with osy deploy, the deploy reports each setting to the console: its value in each mode, and whether
the .env.<mode> file or the Default supplied it. An application's Config tab shows them read-only, per
environment it runs in — the value that environment reads (an application runs in one mode, so every environment reads
the same file, of the version deployed there) and where it is set: .env.production, or Default in source. There
is no edit box, on purpose: the repository is where a setting lives, and a value changed in the console would disagree
with it and lose to the next deploy. Change the file, and deploy.
A value that looks like a credential is not reported — your machine withholds it before the deploy request leaves, and the tab shows it as Not shown, pointing at declaring secrets (app.Secrets), where it belongs. So is any setting that shares its name with a declared secret.
The same tab lists the hosts each package is granted (use Acme.Snow@1 { egress "…"; }), per environment, and an
Open the app button for each environment. Business settings — a shop's name, its prices, its fees — are your
application's own data, changed in the application itself; the console does not keep a copy of them.
Config values and egress grants are shown to whoever manages the application: an owner or admin of its organization, or a member of the application who is not a Developer.
Examples#
Declare two settings with development defaults, and read one in a function that builds an invite link:
app.Config = [
new Setting("InviteBaseUrl") { Default = "http://localhost:8099" },
new Setting("FromAddress") { Default = "noreply@localhost" },
];
string InviteLink(string token) {
return Config.InviteBaseUrl + "/accept?token=" + token;
}To give production a different value, add a checked-in .env.production next to your app.osy:
InviteBaseUrl=https://app.example.com
[email protected]Now Config.InviteBaseUrl reads http://localhost:8099 when the app runs locally and https://app.example.com when it
runs in production — from one compiled build, with no code change.
See also#
- declaring secrets (app.Secrets) — the SECRET twin: a value kept out of source and never served to the client
- embedding provider (app.Embedding) — another
app.Xconfig block (the embedding model) - The Llm kit — call a model, and know what each call cost —
app.Llm, the models an app declares toOsysharp.Llm