rage is a small Rust CLI for using GCP Secret Manager as a personal secrets store
while keeping day-to-day shell startup fast.
The model is:
GCP Secret Manager -> rage sync -> age-encrypted local cache -> rage shell/exec/ssh
GCP Secret Manager is the source of truth. Local shells and commands load from the encrypted cache, so they do not pay a network round trip on every shell init.
Prebuilt binaries are published on every tag push under GitHub Releases. The supported host matrix is macOS aarch64, Linux x86_64, Linux aarch64, and Windows x86_64.
While thehumanworks/rage is a private repository, the installer needs a
GitHub token with contents:read scope on the repo (a fine-grained PAT or any
PAT with repo scope works). The script also falls back to gh auth token if
the gh CLI is already signed in.
For Unix-like systems, the bundled installer detects your platform, downloads
the matching archive via the authenticated GitHub Releases API, verifies its
SHA-256 checksum, and drops the rage binary into a system-wide location:
export GITHUB_TOKEN=ghp_xxx # or: gh auth login --scopes 'repo'
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" \
https://raw.githubusercontent.com/thehumanworks/rage/main/install.sh | shOnce the repo is made public, the token is no longer required and the one-liner becomes:
curl -fsSL https://raw.githubusercontent.com/thehumanworks/rage/main/install.sh | shUseful environment overrides: VERSION=v0.1.0 pins a specific release,
INSTALL_DIR=$HOME/.local/bin selects a writable install dir, and
RAGE_NO_SUDO=1 keeps the script entirely within $HOME when run on a
locked-down machine.
For Windows, download rage-<version>-x86_64-pc-windows-msvc.zip from the
Releases page and add the extracted directory to PATH.
rage is also packaged as a single npm package that downloads the matching
GitHub Release binary during install:
npm install -g @nothumanwork/rageThe npm installer supports macOS aarch64, Linux x86_64, Linux aarch64, and
Windows x86_64. For the private GitHub repository, set GITHUB_TOKEN or
RAGE_GITHUB_TOKEN to a token with contents:read; if neither is set, the
installer falls back to gh auth token when the GitHub CLI is signed in.
Useful overrides: RAGE_VERSION=v0.1.1 pins a release tag,
RAGE_REPO=owner/repo selects another GitHub repository, and
RAGE_NPM_SKIP_DOWNLOAD=1 skips the postinstall download for packaging tests.
- A GCP project with Secret Manager enabled.
GCP_ACCESS_TOKEN,GOOGLE_OAUTH_ACCESS_TOKEN, orCLOUDSDK_AUTH_ACCESS_TOKENset when using remote commands.
Initialize the CLI. For the default file identity path, rage creates the age
identity if it does not exist and derives the public recipient automatically:
rage init \
--gcp-project YOUR_GOOGLE_CLOUD_PROJECT \
--age-identity ~/.config/rage/key.txtIf --gcp-project is omitted, rage init reads RAGE_GCP_PROJECT,
GOOGLE_CLOUD_PROJECT, GOOGLE_PROJECT_ID, or GCLOUD_PROJECT. You can pass
--gcp-endpoint or set RAGE_GCP_ENDPOINT for tests or non-default API
proxies.
This writes ~/Library/Application Support/rage/config.toml on macOS unless
RAGE_CONFIG_DIR is set. The encrypted cache defaults to
~/Library/Caches/rage unless RAGE_CACHE_DIR is set.
Configs from the Infisical-backed version are migrated on first load when a
usable project value is available. New configs store gcp_project.
The generated key.txt contains the private age identity. Keep it local and do
not commit it. The config stores only the derived public age_recipient.
Check GCP Secret Manager auth visibility with a direct token:
GCP_ACCESS_TOKEN=ya29.x rage auth statusrage uses the access token directly and does not invoke gcloud.
RAGE_GCP_PROJECT/GOOGLE_CLOUD_PROJECT can override the stored project at
runtime.
By default, rage reads the age identity from a file. macOS Keychain identity
loading is opt-in and still needs an explicit public recipient:
rage init \
--gcp-project YOUR_GOOGLE_CLOUD_PROJECT \
--age-recipient "$recipient" \
--age-identity acct \
--age-identity-source keychain \
--keychain-service rage-age-identity \
--keychain-account acctWhen the configured identity source is Keychain and rage detects that it is
running inside an SSH session, it refuses to read Keychain unless the command
passes --allow-ssh-keychain explicitly:
rage shell --allow-ssh-keychain globalCreate or update a bundle:
rage set global OPENAI_API_KEY sk-...
rage set project/foo/dev DATABASE_URL postgres://...Fetch remote bundles into the encrypted local cache:
rage sync global project/foo/devStart a shell without more network fetches:
rage shell global project/foo/devRun one command:
rage exec global project/foo/dev -- cargo testExport cached values into the current shell:
source <(rage load global project/foo/dev)The default export output also installs a small shell wrapper so a later
rage unset global OPENAI_API_KEY updates the remote bundle, updates the local
cache, and removes OPENAI_API_KEY from the current shell. If you only want
plain export statements, pass --no-shell-hook.
Open the interactive terminal UI:
rage tuiThe TUI is a thin presentation layer over the same commands documented above.
It lists remote bundles, shows the keys in the selected bundle with values
masked by default (toggle with m), and supports add, edit, delete
operations that go through the existing GCP Secret Manager write and cache paths. It honors
the same SSH/Keychain guard as the other commands and refuses to open when
stdout is not a terminal.
In the agents bundle, imported AUTHLESS_*_JSON auth records are shown as
managed placeholders so imports are visible without rendering raw auth JSON.
rage can import existing Grok and Codex auth files into GCP Secret Manager, refresh
them when needed, and launch the matching CLI with a refreshed provider auth
cache. These records are stored in the agents bundle as
AUTHLESS_<TOOL>_JSON secrets and are reserved from normal
rage load/shell/exec output.
Bootstrap Grok from a logged-in machine:
rage import grok ~/.grok/auth.json
rage grok -- -p "hello"rage grok refreshes the stored record when it is expired or near expiry, then
writes a Grok-compatible auth cache to ~/.grok/auth.json before launching the
child process. Pass -e/--ephemeral to avoid writing the auth cache and use
the child-process GROK_CODE_XAI_API_KEY environment variable instead.
Bootstrap Codex from a ChatGPT login:
rage import codex ~/.codex/auth.json
rage codexrage codex refreshes the stored record when needed, then writes a
Codex-compatible auth.json under ${CODEX_HOME:-$HOME/.codex} before
launching the child process. Pass -e/--ephemeral to remove the managed
auth.json on exit, restoring any file that existed before launch. --force
is retained as a compatibility alias for the same temporary Codex behavior.
Forward selected cached secrets over SSH:
rage ssh myhost project/foo/dev -- printenv DATABASE_URLThe SSH command sends the remote script over stdin (ssh host sh -s) so secrets
are not embedded in the local ssh process arguments.
Bundles are arbitrary names:
global
global/openai
project/foo/dev
machine/macbook
ssh/build-box
Internally, each bundle maps to one GCP Secret Manager secret. The secret ID is the configured prefix plus a base64url-encoded bundle name, and the latest secret version stores that bundle as dotenv text.
- GCP Secret Manager stores the remote secret values. Use a dedicated project and narrow IAM roles where possible.
- The local cache is encrypted with
age. - File identities are the default. macOS Keychain identities are explicit and
require
--allow-ssh-keychainwhenrageis itself running over SSH. rage shellandrage execinject plaintext values into child process environments. That is convenient, but environment variables are still visible to processes with sufficient local access.GCP_ACCESS_TOKENis a bearer credential. Prefer read-only access on remote machines unless writing is required, and rotate it.ragedoes not require thegcloud,age, orage-keygenbinaries.
Default quality gate:
scripts/verify.shNarrow Rust-only gate:
scripts/qa.shLocal end-to-end smoke without GCP Secret Manager:
scripts/smoke-local.shDisposable live GCP Secret Manager smoke:
scripts/smoke-gcp.shAI harness audit:
scripts/harness-audit.shRead AGENTS.md, docs/ARCHITECTURE.md, docs/TESTING.md,
docs/DEFINITION_OF_DONE.md, and docs/AI_HARNESS.md before making
behavioral changes.