Injecting secrets into CI
The usual way to get a secret into CI is to paste it into your provider’s secret store. Then it lives in two places, rotating it means remembering both, and nothing records which pipeline read what.
reoclo run removes the copy. You store one credential in your CI provider — an automation key that can read nothing by itself — and grant that key read access to a project. At run time it resolves the values and injects them into your command as environment variables. Nothing is written to disk, nothing is printed, and every read is attributed to a workflow run and a commit.
This works the same on GitHub Actions, Gitea Actions, GitLab CI, Woodpecker, and your laptop.
Before you start
Section titled “Before you start”You need a secret project with something in it — see the quickstart — and the Admin role to grant access to a key.
-
Create an automation key for this pipeline
In the dashboard, go to API Keys, switch to the Automation Keys tab, and create a key. It begins with
rca_. Copy it now; it is shown once.Give it no servers and no operations. That is not a limitation — resolution is gated by the grant you add next, not by a key’s operations. A key that can do nothing except read one secret project is exactly what belongs in CI, and it means a leaked key cannot be used to deploy anything.
Create one key per pipeline rather than sharing one. Revoking then costs you one pipeline instead of all of them.
-
Grant the key read access to your project
Open the project, go to the Access tab, and add a grant for the Automation key you just created, with
read.This is the step that matters. Without it the key resolves nothing — and, as you will see in step 5, it fails loudly rather than quietly handing your command an empty environment.
Repeat for each project this pipeline needs. Grants are per project; there is no global read.
-
Store the key in your CI provider
Add the
rca_key as a secret namedREOCLO_AUTOMATION_KEY. On GitHub or Gitea that is Settings → Secrets and variables → Actions.This is the only Reoclo credential your CI provider ever holds. The actual secrets stay in Reoclo.
-
Install the CLI and wrap your command
reoclo runresolves every project the key can read and injects the values into whatever follows--:- name: Run migrations with injected secretsenv:REOCLO_AUTOMATION_KEY: ${{ secrets.REOCLO_AUTOMATION_KEY }}run: |curl -sSL https://get.reoclo.com/cli | bashreoclo run -- ./migrate.shInside
migrate.sh, every key in the granted project is just an environment variable. No SDK, no fetch, no parsing.Gitea’s runner is GitHub-Actions-compatible, so that step works there unchanged. For other systems, install the CLI and use the same command:
Terminal window curl -sSL https://get.reoclo.com/cli | bashREOCLO_AUTOMATION_KEY="$REOCLO_KEY" reoclo run -- ./migrate.shTo pin the CLI instead of tracking latest, download a tagged
install.shfrom the releases and verify its checksum before running it. -
Confirm it resolved
Check that a key arrived without printing it:
Terminal window reoclo run -- sh -c '[ -n "$DATABASE_URL" ] && echo resolved'If the grant is missing, this does not print
resolvedand it does not run your command — it fails. That is the whole design:reoclo runfails closed, so a missing grant surfaces as a red pipeline, never as an app that booted with an emptyDATABASE_URL.
Narrowing what gets injected
Section titled “Narrowing what gets injected”With no flags, everything the key can read is injected — so adding a secret to a granted project needs no pipeline change.
Pass -p to restrict it, repeating the flag for more than one project:
reoclo run -p payments-production -- ./migrate.shreoclo run -p payments-production -p shared-infra -- ./deploy.shNaming a project the key cannot read fails without running your command. A project that exists but is not granted and one that does not exist report the same message, so a key cannot be used to enumerate your projects.
Injected values are not masked
Section titled “Injected values are not masked”This is the one thing to internalise.
Your CI provider masks strings you registered with it. Values injected by reoclo run never passed through your CI provider, so it has no idea they are secret. If your command prints one, it lands in the log in the clear — and CI logs are often readable by more people than the secret is.
If you need to prove a value arrived, compare it instead of printing it:
reoclo run -- sh -c 'printf "%s" "$API_KEY" | sha256sum | cut -c1-8'What lands in the audit trail
Section titled “What lands in the audit trail”Each reoclo run opens a resolve session that expires after 15 minutes, and records the session plus the keys it resolved — names only, never values.
On GitHub and Gitea the workflow run id and commit SHA are picked up automatically, so every read is attributable with no extra wiring. Elsewhere, pass the commit yourself:
reoclo run --commit "$CI_COMMIT_SHA" -- ./release.shThat gives you the question most secret stores cannot answer: which pipeline run, on which commit, read this key?
Rotating
Section titled “Rotating”Rotate the secret in Reoclo. The next pipeline run picks it up. There is no CI change and no second copy to remember, because your CI provider never had the value.
Rotating the automation key does need a CI change — replace REOCLO_AUTOMATION_KEY and revoke the old key. Per-pipeline keys keep that blast radius to one pipeline.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause |
|---|---|
| Fails, “no accessible secret projects” | The key has no grants at all. Add one on the project’s Access tab. |
| Fails, “no accessible project matched” | The -p name is not granted to this key, or does not exist. The two are reported identically by design. |
| Exit code 3 | The key is invalid, expired, or revoked. |
| Exit code 4 | Not an automation key — either a reoclo login session, or no credential reached the runner. Check the secret name in your workflow. |
| A variable is empty in your script | Resolution succeeded but that key is not in a granted project. reoclo run cannot fail for a key it was never told about. |
A non-zero exit is not always a Reoclo failure: your command’s exit code is passed straight through, so a script exiting 1 looks like exit 1. The message on stderr is written only by reoclo run itself.
Next Steps
Section titled “Next Steps”reoclo run— full command reference- Environment variable references — for apps Reoclo deploys, skip the pipeline entirely
- CI/CD automation — the wider automation-key surface
- API Keys — creating and scoping automation keys