The values your app reads
The settings, environment variables, and secrets your app reads at startup: what each one is, where it lives, and what a value baked into a static build actually gives away.
A setting, and a secret
Both arrive at your app the same way. What each one is, and who can read it back, are not the same question.
A setting
A value that changes how your app behaves, and is not secret if someone sees it.
Example: A feature toggle, a display name, a region string, a log level.
Read back by: Anyone who can already read your code or your running app's output.
A secret
A value that grants access, and must not leak — to a person, a log, or a public bundle.
Example: An API key, a database password, a signing key, a connection string.
Read back by: Named CustomHosted accounts only, to service or rotate it — see the matrix rows below.
Where a value lives, hop by hop
Every value takes the same path, whether it is a setting or a secret.
- 1
You send the value
Through the quote channel, at onboarding or whenever it changes — never a spreadsheet or a chat message.
You
- 2
It is kept in an encrypted store
Sent once, at onboarding or whenever it changes, and kept in an encrypted store — never copied into a spreadsheet, a chat message, or a second system to keep in sync.
CustomHosted
- 3
It is injected at build time or deploy time
Which one depends on the hosting shape and the value itself — see the build-time-versus-run-time band below.
CustomHosted
- 4
It lands in the process environment at startup
The running app reads it at startup. It is never written into a file in your repository, so an agent reading the codebase sees the name of the variable your code uses, never the value stored in it.
The process
- 5
Your code reads it
The same way any app reads a setting or a secret — by name, from the environment it is running in.
You
A build-time value is a public value
Said plainly, because nowhere else on this site has: a value baked into a static bundle ships to every visitor who opens it.
A build-time value ends up inside the bundle
A static site or a single-page app has no server standing by at request time. Anything read into it at build time is baked into the files a browser downloads.
That bundle is public
Every file shipped to a visitor's browser can be read by that visitor. A value baked in at build time is public the moment it ships — not hidden, only unlabelled.
A run-time value stays on the server
A dedicated instance keeps a standing process. A value read from its environment at startup never leaves that process unless your own code sends it somewhere.
The rule that follows
Never put a secret in a build-time value on a static site. A setting is fine there. A secret needs a standing process to hold it — see the shape table below.
What holds a value, by hosting shape
Whether a change needs a rebuild or a restart depends on the shape, not the value.
| Shape | Holds | To change one |
|---|---|---|
| Static Starter ST-STARTER | Build-time values only, baked into the deployed files. | A rebuild and redeploy — nothing to restart, because nothing stands running. |
| Static Pro ST-PRO | The same build-time values, once per environment — production and staging each hold their own. | A rebuild and redeploy of the environment you changed. |
| Dedicated M DD-M | Run-time values in the process environment, read fresh at every startup. | A restart of the process — see the restart-visibility table below for what you would notice. |
| Custom CU-0 | Whichever mix the design calls for — sized and confirmed at the quote stage. | Whatever the design calls for — confirmed once, not guessed at. |
What "restart" looks like on a running process, by shape:
Static site
None — there is no running process to restart.
Dedicated instance
A few seconds while the process restarts, confirmed by the health check above before the window closes.
Managed database, minor version
A brief connection blip during failover — existing connections reconnect on their own.
Changing a value
Sized inside the iteration allowance — around 120 minutes, the same 2-hour unit any small change draws from.
- 1
Send the new value
Through the quote channel, naming which variable and which environment.
You
- 2
It is stored, and rotated in
Stores the value you send and rotates it on request.
CustomHosted
- 3
It takes effect
A rebuild on a static shape, or a restart on a dedicated shape — see the restart-visibility table above.
CustomHosted
- 4
You are told it is done
A note confirming what changed, in the same shape as every other note on this site.
CustomHosted
To go back, send the previous value the same way — there is no separate undo button for a configuration change.
Production values, staging values
Same infrastructure definition, different values — see Environments for the environment itself.
Production
Its own set of values — the ones your live app actually reads.
Staging
Its own separate set, not a copy of production's. Changing one never quietly changes the other.
Rotating a value
Three real triggers, each with a place that owns it in full.
A person leaves
Anything they held a key to on our side is revoked the same day — the same rule joiners and leavers already sets. See Access — joiners and leavers.
A value leaks
Treated as a live incident: the value is rotated, and the exposure is worked the way any leaked credential is. See Under attack — password guessing.
A scheduled rotation
Send the replacement value whenever you choose — there is no console click, only the same quote channel as any other change. See Access — secrets.
Nothing extra
A configuration change is priced the same as any other small change.
A config change sits inside the iteration allowance — 3 changes a month, 2 hours each, the same allowance covered under Agentic iteration — CodeHerder ($25/mo). See The hours ledger for the full ledger.
This site's own configuration
Every row below is provable from this repository, not asserted.
One build-time value, set on purpose
PUBLIC_CONTACT_ENDPOINT is read in at build time and ships inside this site's own static bundle — public by design, because it only names where the quote form posts to.
No secret anywhere in the repository
This site holds no API key, password, or signing key of its own. The quote form itself carries no secret — it posts to a relay that holds the credential instead.
A relay, not a direct write
The quote form sends a request to a relay rather than writing anywhere directly, so nothing this site's own code runs ever needs a stored credential.
The honest limits
Not buried in a footnote — the same discipline as every other honesty band on this site.
No self-serve console
There is nowhere to click a value in or out yourself. Every change goes through the quote channel, the same as any other request on this site.
A build-time value in a static bundle is public
Said plainly, not buried: baking a secret into a static build ships it to every visitor's browser. Use a run-time value on a standing process instead.
We can read a value back
Named accounts only, to service or rotate a value. A named account, never a shared login.
No per-branch configuration sets
Production and staging each hold their own values. There is no wider fleet of per-branch value sets on a standard plan.
No feature-flag service
A setting can toggle behaviour, but there is no separate flag service here to turn one on or off per visitor. That is application code, not a platform feature.
A name, never a value
This is the exact boundary CustomLabs draws for the CodeHerder agent — the one that makes agentic iteration safe to run against your live code.
It sees that your code reads a variable, never the value stored in it.
A briefed change that needs a new value comes back as a pull request naming the variable, not the value in it. A person sets the value once the change is reviewed — the same ship mechanics as any other briefed change:
- 1
A routine scan flags a dependency
The same kind of check that watches the infrastructure layers above, run against your application’s own dependency list.
CustomHosted
- 2
It's triaged as application-level, not infrastructure
A library your own code imports isn't ours to patch silently — it can change how the app behaves.
CustomHosted
- 3
Briefed as a change
Written up the same way any other change request is — this is what the Agentic iteration — CodeHerder add-on covers.
CustomLabs
- 4
An agent opens the pull request
The upgrade lands as a real pull request against the repository, with the test suite run against it.
CodeHerder
- 5
A human reviews and merges
Someone reads the diff before it goes anywhere near production — nothing here is auto-applied blind.
CustomLabs
- 6
It ships through the same pipeline
The same pipeline that deploys everything else on your stack ships this too — no separate process to babysit.
CustomHosted
- 7
You're told it's done
A note confirming what changed and that it merged, in the same shape as every other note on this site.
CustomHosted
Configuration questions
What people ask once "an environment variable" stops being self-explanatory.
01What counts as a secret, rather than just a setting?
Anything that grants access on its own — an API key, a password, a signing key, a connection string. If seeing the value would let someone do something they should not, it is a secret.
02Can I read my own secret's value back?
There is no console to read it from — ask, and we tell you.
03Is it safe to put an API key in a static site's build?
No. A static build ships to every visitor's browser, so anything baked in at build time is public. Keep a secret on a dedicated shape that reads it at run time instead.
04Does changing a value take my app down?
On a static site, a rebuild replaces the deploy — nothing was running to go down. On a dedicated instance, the process restarts — see the restart-visibility table above for exactly what that looks like.
05Do staging and production share the same values?
No. Each environment holds its own set, so a value changed in one never quietly changes the other.
06Can the CodeHerder agent read or set a value?
No, on both counts. It can see that your code reads a variable by name. It can open a pull request that adds a new one. It never sees or sets the value stored in it.
07What does a config change cost?
Nothing on top. It sits inside the iteration allowance — 3 changes a month, 2 hours each, the same allowance any other small change draws from.
What configuration connects to
- Logins
An identity provider's client secret is exactly the secret this page defines — held the same way as any other.
- Access
Who can read a secret's value back, and who can set one — the matrix rows this page quotes whole.
- Repository
The one real hazard inside the repository itself — a committed secret — and what happens when we find one.
- Environments
The second environment itself — its address, who can reach it, and what data it holds.
- Patching
The briefed-change mechanics this page's CodeHerder section reuses in full.
- Databases
The connection string as a value — this page only names where it is read from.
- Email
The relay credentials as a value — the same treatment as any other secret.
- Under attack
What a leaked value turns into, and the escalation this page's rotation section links to.
- The hours ledger
The iteration allowance a config change is priced from — the same row this page's cost section quotes.
- The Loop
The "config change" the loop ships, in the wider build-iterate-run picture this page is one step of.
- Deploys
The build-time-versus-run-time trap above, from the other side — what a deploy actually does between a merge and a value being read.
- Outside services
Where the key this page stores actually gets used — the call it unlocks, and what happens when the far end changes or breaks.
- Forms
The build-time-versus-run-time trap above, applied to a static page: no secret can live in a form's own markup.
Need a value changed, or a new one added?
Brief it through the same quote channel as any other request — see Access for who can read one back.