CI secret injection
A build needs real secrets — a Stripe key, a database URL — to deploy. The usual answers are bad:
commit a .env file (now your secrets are in git history forever) or paste them into each CI
platform’s secret store (now they’re scattered across GitHub, GitLab, Vercel, and a wiki, with no
shared rotation and no shared audit trail). veil run replaces all of that with one audited,
centrally-rotatable, revocable source.
You declare which environment variables map to which Veil services, and veil run resolves them and
injects them into your command at the moment it runs — into that child process only, masked out of its
output, never written to disk.
If you’ve used 1Password’s op run, this is the same model — pointed at Veil.
The manifest
veil run reads a .veil.json manifest that maps each environment variable to a service (or a
specific field of one, with service/field):
{
"STRIPE_KEY": "stripe",
"DATABASE_URL": "prod-db/dsn"
}
At runtime, each value on the right is resolved from Veil and set as the env var on the left — but only
inside the child process veil run spawns. Your shell never sees the plaintext, and nothing is ever
written to a file.
Run it
Mint a CI token in the Veil app (it starts with veil_ci_) and provide it as VEIL_TOKEN:
Want to see what would be injected without exposing anything? veil run --print shows a masked
preview of the resolved variables:
What you actually get
veil run does several concrete things, every run:
- Resolves secrets from Veil over TLS at runtime — nothing is baked into the image or the repo.
- Injects them as environment variables into the child process only.
- Masks them out of the child’s
stdoutandstderr, so a secret that’s accidentally echoed in a log line comes out redacted. - Never writes them to disk — no temp
.env, no cache file.
See the full CLI reference for flags, and tokens for how CI tokens differ from agent and SSH tokens.