Skip to content

reoclo run

reoclo run [options] -- <command...>

Resolves every Secrets Manager value your credential can read, injects them into a child process as environment variables, and runs it. The credential is an automation key or a machine user token. Values are never written to disk and never printed. The child’s exit code is passed straight through, so reoclo run is transparent to whatever CI system is watching it.

This is how you consume managed secrets in any pipeline: GitHub Actions, Gitea Actions, GitLab CI, Woodpecker, or a local script.

Terminal window
REOCLO_AUTOMATION_KEY=rca_... reoclo run -- ./migrate.sh
REOCLO_MACHINE_TOKEN=rk_m_... reoclo run -- ./migrate.sh

Everything after -- is the command. Use -- even when it looks unnecessary. Without it, flags meant for your command are parsed as flags for reoclo.

FlagDescription
-p, --project <name>Only inject this project. Repeatable. Omit to inject every project the key can read.
--env-file <path>Render a template file of op:// references and inject the resolved values. Cannot combine with -p. See Template files.
--commit <sha>Record a commit SHA on the audit entry.

reoclo run requires one of two credentials: an automation key (rca_, read from REOCLO_AUTOMATION_KEY) or a machine user token (rk_m_, read from REOCLO_MACHINE_TOKEN). An interactive reoclo login session is still rejected: this command exists to hand values to machines, not people. See Machine Users for how the two credentials differ.

Terminal window
REOCLO_AUTOMATION_KEY=rca_... reoclo run -- ./migrate.sh
REOCLO_MACHINE_TOKEN=rk_m_... reoclo run -- ./migrate.sh

The credential needs a read grant on each project you want injected. A tenant admin adds one from the project’s Access tab. See Granting access.

For an automation key, only the grant matters. A key restricted to no servers and no operations still resolves secrets normally. That makes a dedicated, otherwise-powerless key the right thing to put in CI.

A secret project restricted to specific servers cannot be read by reoclo run using a machine user token, whatever its own server scope. Use an automation key for that project.

With no -p, every project the key is granted is injected. That is usually what you want, and it means adding a secret to a granted project requires no pipeline change.

Pass -p to narrow it. Repeat the flag for more than one:

Terminal window
reoclo run -p payments-production -- ./migrate.sh
reoclo run -p payments-production -p shared-infra -- ./deploy.sh

Naming a project the key cannot read is an error, and the command fails without running your child process. A project that exists but is not granted and a project that does not exist are reported identically, so a key cannot be used to probe which projects exist.

--env-file injects the exact keys a template names, instead of every granted project. The template is a .env file whose values are op:// references:

Terminal window
# app.env.tpl
DATABASE_URL=op://payments-production/app/DATABASE_URL
STRIPE_API_KEY=op://payments-production/app/STRIPE_API_KEY

Each reference is op://<project>/<item>/<field>. Reoclo maps it to the secret <field> in the project <project>. It ignores the <item> segment. That segment keeps the file compatible with 1Password’s op inject.

Terminal window
REOCLO_AUTOMATION_KEY=rca_... reoclo run --env-file app.env.tpl -- ./migrate.sh
REOCLO_MACHINE_TOKEN=rk_m_... reoclo run --env-file app.env.tpl -- ./migrate.sh

The template names its own projects through the reference vaults. So --env-file cannot combine with -p. Passing both is exit code 2.

Only a whole value is a reference. An op:// in a comment, or in the middle of a value, stays as plain text. A value that is not a reference passes through unchanged, so one template can mix references and plain values.

Reoclo resolves the references, never 1Password. The op command does not need to be installed. This is different from secrets import, which reads your real 1Password items.

A resolved value overrides an inherited environment variable of the same name. If your runner already exports DATABASE_URL and the granted project also defines DATABASE_URL, the child sees the managed one.

Each run opens a short-lived resolve session (15 minutes) and records two audit entries: the session being opened, and the keys that were resolved. Only key names are recorded, never values.

On GitHub Actions and Gitea Actions, GITHUB_RUN_ID and GITHUB_SHA are picked up automatically, so runs are attributable to a workflow and a commit with no extra wiring. Elsewhere, pass --commit yourself:

Terminal window
reoclo run --commit "$CI_COMMIT_SHA" -- ./release.sh

reoclo run fails closed. If resolution fails for any reason (no grant, a project you cannot read, an expired key), the child process does not run. It will never silently execute your command with the secrets missing.

Run a migration with every granted secret injected:

Terminal window
REOCLO_AUTOMATION_KEY=rca_... reoclo run -- ./migrate.sh
REOCLO_MACHINE_TOKEN=rk_m_... reoclo run -- ./migrate.sh

Log in to a registry without the password ever reaching your shell history or the process table:

Terminal window
reoclo run -p shared-infra -- sh -c 'echo "$DOCKERHUB_PASSWORD" | docker login --password-stdin -u "$DOCKERHUB_USERNAME"'

Check that a key is present without revealing it:

Terminal window
reoclo run -- sh -c '[ -n "$STRIPE_API_KEY" ] && echo present'
CodeMeaning
0Success (or the child exited 0)
2Bad arguments: no command after --, a bad --env-file path, a malformed op:// reference, --env-file with -p, or a token in the wrong environment variable
3Authentication failure: the credential is invalid, expired, or revoked
4An interactive session (neither an automation key nor a machine user token)
6Resolution failed: no grant, no readable project matched -p, or a template reference did not resolve. The child never started.
7The control plane was unreachable: DNS failure, connection refused, or timeout
nAnything else is the child’s own exit code, passed through

Resolution failure is 6, deliberately not 1. Because run passes the child’s exit code through, a resolution failure sharing code 1 with “my migration script exited 1” would make the two indistinguishable, which is the one branch a pipeline most needs:

Terminal window
reoclo run -- ./migrate.sh
case $? in
0) echo "migrated" ;;
6) echo "secrets did not resolve — check the key's grants" ;;
7) echo "control plane unreachable — safe to retry" ;;
*) echo "migrate.sh itself failed" ;;
esac

The ambiguity is not gone entirely, just moved somewhere it rarely bites: a child that itself exits 6 or 7 is still indistinguishable from Reoclo’s own. Only reoclo run writes to stderr, so that remains the tiebreaker.