Agent-readable docs index: /llms.txt. Full docs in one file: /llms-full.txt. Download /docs.zip to grep all markdown files locally.

secrets manager forhumans & agents.

Login with GoogleLearn more

rx

Self-hostable secrets manager. CLI + Cloudflare Workers backend.

rx replaces .env files with a cloud-based secrets manager you self-host. Prefix commands with rx run — secrets inject as env vars, never written to disk, redacted from output.
# before source .env && next dev # after rx run -- next dev
Open-source alternative to Doppler / Infisical. Runs on Cloudflare Workers + D1. MIT.

Install

curl (downloads native binary to ~/.local/bin):
curl -fsSL https://env.roosterx.vn/install.sh | bash
From repo (requires Zig 0.15.1 + pnpm):
git clone <repo> cd cli pnpm install pnpm build:zig cp zig-out/bin/rx ~/.local/bin/rx codesign --force --sign - ~/.local/bin/rx # macOS only

Quick start

# 1. Login to your self-hosted instance rx login --api-url https://env.roosterx.vn # 2. Pick project + env for this directory rx setup # 3. Run app with secrets injected rx run -- next dev

Why rx

  • Self-hosted — your secrets on your Cloudflare account, no vendor lock-in
  • No .env files — secrets live in the cloud, share across machines
  • Output redaction — high-entropy values replaced with * in stdout/stderr
  • Agent-safe — AI agents never see raw secrets via rx run
  • Multi-env — dev / preview / prod in one project, switch with -c prod
  • Audit log — append-only event log tracks every secret change
  • AES-256-GCM — every secret encrypted at rest with random IV
  • REST API — OpenAPI-documented, bulk set/get/download

CLI reference

rx login

rx login # interactive device flow rx login --api-url https://env.roosterx.vn # custom instance rx login --token sig_xxx # save existing token

rx setup

Save default project + environment for current directory (stored in ~/.rx/config.json, not in repo):
rx setup # interactive picker rx setup --project proj_abc --env dev # non-interactive

rx run

Execute a command with secrets injected as environment variables:
rx run -- next dev # from setup config rx run -c dev -- next dev # explicit env rx run --command 'echo $MY_SECRET' # shell string rx run --mount .env -- npm start # write to file, clean up on exit rx run --disable-redaction -- ./script.sh # opt out of redaction
Shell features (&&, pipes, $VAR) require --command:
# wrong: parent shell expands $VAR before rx starts rx run --command "psql $DATABASE_URL -c 'select 1'" # right: $VAR expands inside rx's child shell rx run --command 'psql $DATABASE_URL -c "select 1"'

rx secrets

rx secrets # list names rx secrets get DATABASE_URL # get value rx secrets set API_KEY sk-live-xxx # set rx secrets set CERT < cert.pem # from stdin rx secrets delete OLD_KEY # delete rx secrets download --format json # bulk export rx secrets download --format env > .env.prod # export as .env

rx projects / rx environments

rx projects create --org org_id --name my-app rx environments create --project proj_id --name Staging --slug staging

Download formats

--format flag for secrets download: json, env, yaml, docker, dotnet-json, xargs.

Global flags

FlagEnv varDescription
--token <sig_xxx>RX_TOKENBearer token for auth
--api-url <url>RX_API_URLAPI endpoint
-c <slug> / --env <slug>RX_ENVIRONMENTEnvironment slug
-p <id> / --project <id>RX_PROJECTProject ID

Integrations

Cloudflare Workers:
rx secrets download -c prod --format env | wrangler secret bulk --env prod /dev/stdin
CI / GitHub Actions:
- name: Build with secrets env: RX_TOKEN: ${{ secrets.RX_TOKEN }} RX_PROJECT: ${{ vars.RX_PROJECT }} RX_ENVIRONMENT: production run: | npx rx run -- next build
Docker:
rx secrets download --format docker > .env.docker docker run --env-file .env.docker my-image

Self-hosting

Two Cloudflare Workers + D1 databases. Monorepo with pnpm workspaces.
git clone <repo> cd rx && pnpm install

App Worker (required)

echo "BETTER_AUTH_SECRET=$(openssl rand -base64 32)" > app/.dev.vars pnpm --dir app dev # local pnpm --dir app deployment # deploy preview pnpm --dir app deployment:prod # deploy production
Uses hosted auth at auth.sigillo.dev by default (no Google OAuth needed).

Provider Worker (optional, for air-gapped setup)

Requires Google OAuth credentials:
echo "BETTER_AUTH_SECRET=$(openssl rand -base64 32)" > provider/.dev.vars echo "GOOGLE_CLIENT_ID=..." >> provider/.dev.vars echo "GOOGLE_CLIENT_SECRET=..." >> provider/.dev.vars pnpm --dir provider dev pnpm --dir provider deployment pnpm --dir provider deployment:prod
Then set PROVIDER_URL in app/wrangler.jsonc and redeploy app.

Architecture

Your Machine Cloudflare ───────────────── ────────────────────── rx run -- next dev ───► App Worker (your secrets) │ • Secrets CRUD │ device flow login • AES-256-GCM encrypt │ or bearer token • Audit log ▼ • API tokens CLI (Zig binary) • Web UI • D1 database │ Provider Worker (auth.sigillo.dev) • Google login • OAuth2 / PKCE • Dynamic client registration
Secrets encrypted with AES-256-GCM before storage (random 12-byte IV per write). Encryption key from ENCRYPTION_KEY env var, or derived from BETTER_AUTH_SECRET via SHA-256.

REST API

OpenAPI docs at /api/openapi.json on your self-hosted instance.
# list secrets curl -H "Authorization: Bearer sig_xxx" \ https://your-instance/api/environments/{envId}/secrets # bulk set curl -X PUT -H "Authorization: Bearer sig_xxx" \ -H "Content-Type: application/json" \ -d '{"secrets": {"KEY1": "val1"}}' \ https://your-instance/api/v0/projects/{projId}/environments/{envId}/secrets

License

MIT