Variables and secrets
Variables are configuration values your pipelines need at runtime: non-secret values like DEPLOY_REGION and secret credentials like HARBOR_PASSWORD. Loha stores both in a single unified model, encrypts secret values at rest with AES-256-GCM, and exposes them to build pods according to a clear precedence rule.
Scopes
A variable lives in one of four scopes. Most-specific scope wins when the same name appears at more than one level.
| Scope | Where it applies | Where to manage |
|---|---|---|
| Project | Every build in the project | Project → Variables |
| Repository | Every build for a single repo. Overrides project. | Repo → Variables tab |
| Environment | Jobs that target one of the project's environments. Overrides repository and project. | Project → Variables → the environment's tab |
| Trigger | Builds fired by a single trigger. Overrides environment, repository and project. | Trigger detail page → Variables section |
The environment scope is the natural home for values that differ between staging and production: set DEPLOY_TOKEN once on the prod environment, and every job that targets prod gets it, whichever trigger started the build. See Environment variables. The trigger scope is still the tool for a value that belongs to one trigger only.
Trigger variables work on any trigger, whether it is bound to a repository or runs standalone (a manual or generic-webhook trigger with no repo). For a repo-less trigger they are the only per-trigger configuration: its builds resolve project and trigger variables only, with no repository tier in between. Manage a trigger's variables in the Variables section on its detail page.
Variables vs. secrets
Every variable has a Visibility of either Plain or Secret, chosen when you create it.
- Plain: stored as plain text. Loha shows the value back in the dashboard for editing and writes it directly into the build pod's env block.
- Secret: encrypted at rest with AES-256-GCM. The dashboard never displays the value back after creation; you can overwrite it but not read it. Secret values are also redacted from build log streams before they reach the dashboard.
Visibility is fixed once a variable is saved. To switch a variable between Plain and Secret, delete it and recreate it.
Both kinds of variables resolve through the same precedence chain. The only behavioural difference is how a secret reaches a build step (see Using secrets below).
Resolution precedence
When a build step runs, Loha builds its environment in this order: each layer overrides the previous:
- Project variables
- Repository variables
- Environment variables (when the job targets an environment)
- Trigger variables (when a trigger fired the build)
env:at the top of.loha.ymlenv:on the jobenv:on the step- Secrets the step explicitly named in
secrets: [NAME]
Precedence is worked out per job: two jobs of the same build that target different environments each get their own environment's values.
A user-set step env value wins over a same-named secret: explicit beats implicit.
Using a non-secret variable
Non-secret variables are automatically available to every step's env block. Nothing to declare in the YAML.
# DEPLOY_REGION is set at project or trigger scope.
jobs:
deploy:
steps:
- name: Deploy
run: ./deploy.sh --region "$DEPLOY_REGION"
Using a secret
Secrets are explicit reference only. They never appear in a step's env unless the step names them.
jobs:
build:
steps:
- name: Push to registry
uses: loha/buildah-build@v1
with:
IMAGE: registry.example.com/acme/web:${COMMIT_SHA}
secrets:
- HARBOR_USER
- HARBOR_PASSWORD
The push step sees HARBOR_USER and HARBOR_PASSWORD as env vars. Other steps in the same pipeline do not: injection is per-step.
If a step references a secret that doesn't exist in the build's scope chain, Loha attaches a warning to the run and the env var is omitted (the step is free to fail naturally if it needs it).
Store a service account key as a secret named LOHA_TOKEN and reference it with secrets: [LOHA_TOKEN]. The CLI picks it up automatically. See Using the CLI in CI/CD.
Environment variables
An environment variable belongs to one of the project's environments (for example staging and prod). Only jobs that target that environment receive it, so a production deploy token never reaches a test job.
Environment variables use the environments you create for deployments, so they are available only in workspaces where deploy targets are enabled. If Project → Variables shows no environment tabs, ask a workspace administrator. The feature is being rolled out workspace by workspace.
Managing them
Open Project → Variables. Next to the Project tab there is one tab per environment of the project. Pick an environment's tab and add rows exactly as in Adding a variable. Plain and Secret visibility work the same way, and secrets are still explicit reference only.
Choosing a job's environment
A job targets an environment in one of two ways:
-
environment:on the job in.loha.yml:jobs:
test:
steps:
- name: Test
run: npm test
deploy:
needs: [test]
environment: prod
steps:
- name: Deploy
run: ./deploy.sh
secrets: [DEPLOY_TOKEN]Here only
deploysees theprodvariables and can name theprodDEPLOY_TOKENsecret.testgets project, repository and trigger variables only. In the Pipeline Builder, the same setting is the job's Environment field. -
The trigger's default environment. On the trigger's detail page, set Default environment. Every job of that trigger's builds that does not set
environment:then targets it. A job's ownenvironment:always wins over the default.
A job with neither targets no environment.
The environment is looked up by name in the build's own project. A name the project doesn't have fails the build before any step runs, naming the environments the project does have. Loha never falls back to running the job without the environment's values. See errors.
The build page shows an environment badge next to each step whose job targeted one.
Trigger-scope example (staging vs. prod)
You have one repository with two triggers, staging-deploy and prod-deploy. Both run the same .loha.yml, but you want each to push to a different cluster.
- On the
staging-deploytrigger's detail page, add a Plain variableDEPLOY_TARGET=staging.example.comin the Variables section. - On the
prod-deploytrigger's detail page, addDEPLOY_TARGET=prod.example.com. - In
.loha.yml, just read$DEPLOY_TARGET: no conditionals, no hardcoded URLs.
For secret credentials that differ between environments (e.g. each cluster has its own deploy token), you can create the same-named secret on each trigger and reference it via secrets: [DEPLOY_TOKEN]. Where environment variables are available, putting DEPLOY_TOKEN on the staging and prod environments instead, and giving each trigger the matching Default environment, keeps the token in one place per environment.
Adding a variable
Variables are managed in an inline editable table.
- Open the variables table at the appropriate scope: Project → Variables (project; pick an environment's tab for an environment variable), the repo's Variables tab (repository), or the Variables section on a trigger's detail page (trigger).
- Fill in the blank row at the bottom of the table: a name (uppercase, it auto-capitalizes, alphanumeric and underscore, starting with a letter) and a value.
- Set Visibility to Plain or Secret. A Secret value is masked in build logs automatically.
- Add more rows as needed, then click Save changes in the sticky footer to commit them all at once.
Editing works the same way: change a value inline and save. A saved variable's name and Visibility cannot be changed. Delete the row (trash icon) and recreate it instead. You can discard unsaved edits with Discard.
Only OWNER and ADMIN roles can create or modify variables. Members see the table read-only.
Bulk edit from a .env blob
To add many variables at once, click Bulk edit above the table and paste a .env-style blob: one KEY=value per line. Loha turns each line into a table row:
export KEY=valueprefixes are accepted, and matching surrounding quotes are stripped.- Blank lines and
#comment lines are skipped. - Append
# secretto a line to mark that variable Secret, or tick Treat all as secret to flag every pasted line. - A pasted name that already exists updates that row's value; new names are added as rows.
Pasting only stages the rows; nothing is written until you click Save changes.
Encryption model
- Secret values are encrypted with AES-256-GCM.
- The master key lives in an in-cluster secret store and never leaves the cluster.
- Secret values are redacted from build log streams before they reach the dashboard.
- Non-secret (Plain) values are stored as plain text and intentionally displayed back in the UI.
- At build time a secret value reaches a step as a Kubernetes Secret reference, not as a literal in the pipeline spec. Loha creates one Secret for that run. It lives as long as the run's PipelineRun, not only until the run ends: it is removed when the run's record is pruned.
Audit
Every variable create / update / delete is recorded in the workspace audit log with actor, timestamp, and target. Each build that uses secrets logs a secret_access event listing which secrets it read, and which environments the build targeted.
What's not yet supported
- Master-key rotation UI is in v0.6.0.
- Customer-managed secret backends (bring-your-own secret store) are v1.1.0.
- Run-scoped tokens: the cache token, the artifact token, the pipeline-approval token, the security-scan upload credential and the Build Security final-gate token are still passed to a step as literal values. They are issued per run and short-lived, and they expire with the run's time limit.
- Approval gates on environments (e.g. a required review before a job targeting
prodruns) are a separate roadmap item. Targeting an environment today selects its variables; it does not ask anyone for approval.
See the roadmap for the timeline.