Ci
# GitHub Actions and Forgejo Actions
The same job body works in .github/workflows/envvault.yml or .forgejo/workflows/envvault.yml.
Use a trusted runner with Python 3.11, age and gitleaks installed. Pin third-party actions
to reviewed commit hashes in your organization's runner configuration. This example uses
no third-party action; the runner must supply a checked-out repository first.
name: private-environment
on: [workflow_dispatch]
jobs:
run:
runs-on: self-hosted
permissions:
contents: read
steps:
- name: Run trusted app with local environment
env:
ENVVAULT_IDENTITY: ${{ secrets.ENVVAULT_IDENTITY }}
shell: bash
run: |
set +x
umask 077
key="$RUNNER_TEMP/envvault-job.key"
trap 'rm -f "$key"' EXIT
printf '%s\n' "$ENVVAULT_IDENTITY" > "$key"
python cli.py run -i "$key" app.age -- python app.py
Do not enable this job for untrusted pull requests. The identity is a plaintext key file
in the runner's restricted temp directory; trap removes it after the command. Secret
values from app.age are never written to a plaintext file by Env Vault.
For a commit-range check in CI, stage the changed dotenv files in an isolated checkout
and run `python cli.py check-staged`. Do not run `git add` against a developer's index.
## Rotation reminder
Schedule `envvault inventory app.age -i ci.key` on your trusted runner. Store only its
JSON output in the evidence artifacts. Alert when any variables[].past_due is true.
A weekday cron example is `0 9 * * 1-5`; scheduling is your runner's responsibility.
## Coolify env import
The authorized SaaS terminal already owns /root/scripts/coolify_env_setter.py. Run it
through an adapter with `envvault run -i ci.key app.age -- python your_import.py`.
Inside the adapter, read os.environ into memory and call the fleet setter's public
function. Do not print values, use shell tracing or dump the environment. See
scripts/coolify_import.py: it maps chosen names to setter.set_env without an API call
unless explicitly run by the deploy terminal. Local tests inject a fake setter only.
The host setter is the only integration path; this build never invokes it against Coolify.