Reference
What your app needs to know at run time, the database, the domain, the model, its own name, arrives as environment variables when it is deployed. The repository only declares that they are needed, so it can be published as a template without carrying anyone’s values.
An application needs to know things it cannot know when it is written: which database, which domain, which model, what it is called. In Thinkube those things arrive as environment variables, and the repository holds only the declaration that they are needed.
This split lets you publish the app as a template without your values.
Four channels
All four reach your code as environment variables. None requires a placeholder in your source.
| Channel | Declared in | Answered by |
|---|---|---|
Deploy-time parameters |
|
The person deploying, in the form |
Application variables |
|
The template’s default, or the deployer |
Service dependencies |
|
The platform, resolving another service’s URL |
Secrets |
|
The Secrets page in Thinkube Control; the value never enters the repository or its git history |
A deploy-time parameter is the one to reach for when the answer differs per
deployment and the deployer knows it. tkt-stable-diffusion declares one:
parameters:
- name: model_id
type: str
default: "stabilityai/stable-diffusion-xl-base-1.0"
pattern: "^[a-zA-Z0-9-]+/[a-zA-Z0-9._-]+$"
The deployer is asked for model_id, with that default and that validation, and
the container receives MODEL_ID. The template carries the question; your
deployment carries the answer.
The platform adds its own variables on top: APP_NAME, APP_TITLE, APP_URL,
API_BASE_URL, DOMAIN_NAME, the KEYCLOAK_* set, DATABASE_URL and the
credentials for any service you declared. Those credentials are Kubernetes Secrets
the platform writes through the API at deploy time. k8s/ names the Secrets and
holds no credential, so the repository and its git history carry none.
Secrets
A token or API key is declared by name in manifest.yaml, and its value lives only in the Secrets page of Thinkube Control:
secrets:
- name: HF_TOKEN
description: Hugging Face token for gated models
required: false
The name is the variable the containers receive. required defaults to true.
When the app is deployed, or when a commit changes thinkube.yaml or manifest.yaml, the platform builds one Kubernetes Secret, <app>-secrets, in the app’s namespace. It holds only the secrets this app declares. Every container reads it through envFrom, so each value arrives under its own name.
A required secret the Secrets page does not have stops the deploy, and any commit that needs it:
Secret 'SECRETS_CHECK_TOKEN' is required by secrets-check and is not in the Secrets store Add it on the Secrets page: https://control.thinkube.com/secrets
An optional secret the page does not have sets no variable.
The Secrets page lists the apps that use each secret. Changing a value there writes it into the Secret of every app that uses it and restarts those apps, so their containers read the new value.
How the browser gets a value
A backend reads os.environ directly. A browser application needs one more step.
Its bundle is built before the deployment exists, and by the time it runs it is a file served by a web server to somebody else’s computer. The container’s environment is on the server; the code is on the client. Nothing carries a value across that gap on its own.
publicEnv lists the variables the browser may read. It is declared per container:
containers:
- name: frontend
publicEnv:
- APP_TITLE
The platform passes those names to the container as PUBLIC_ENV_VARS. Before
nginx starts, public-config.sh reads them from the environment and writes them
into config.js, which index.html loads before the bundle. The application
reads them from there:
const title = publicValue('APP_TITLE') || t('app.title')
Any variable the container has can be named — one the platform set, one from
spec.env, one wired from a dependency, or a deploy-time parameter. The list
holds names, never values, which is why it is safe for a published template to
carry it.
Only the names you list reach the browser
Every container receives the whole environment, including POSTGRES_PASSWORD, KEYCLOAK_CLIENT_SECRET, ADMIN_PASSWORD and SEAWEEDFS_SECRET_KEY. publicEnv names exactly what the page may read, and nothing else is written to config.js:
-
A container without
publicEnvpublishes nothing. The browser sees no variable until you name one. -
Any variable can be named, whoever set it. The ones a frontend most wants,
APP_TITLE,API_BASE_URLandKEYCLOAK_CLIENT_ID, are set by the platform, and you list them by name like your own. -
A declared secret stays on the server. A name declared under
secrets:inmanifest.yamlis refused inpublicEnvat deploy time. Any other value you list is public: everyone who opens the app can read it.
Choosing where something belongs
| The value… | Belongs in |
|---|---|
differs per deployment, and the deployer knows it |
|
is part of what the app is, with a sensible default |
|
is another service’s address |
|
must never be in the repository |
|
is the app’s own behaviour, the same everywhere |
plain code — not a variable |
is read by the browser |
any of the above, plus the container’s |
A value a user of the running app changes, such as a setting on a preferences page, is application data. It belongs in the database, served by the backend.