21 stable releases
| new 2026.5.4 | May 16, 2026 |
|---|---|
| 2026.5.1 | May 15, 2026 |
| 2026.3.1 | Mar 9, 2026 |
| 2026.2.6 | Feb 20, 2026 |
| 2025.12.5 | Dec 27, 2025 |
#249 in Authentication
175KB
4K
SLoC
opz
opz is a small wrapper around the 1Password CLI. It finds items, turns valid field labels into environment variables, and can run a command with those secrets injected.
Features
- Search 1Password items by title keyword.
- Check
opauthentication, optional CLI dependencies, and plaintext.env-style credential files withdoctor. - Show item field labels that are valid shell environment variable names.
- Run a command with secrets from one or more 1Password items.
- Generate env files containing
op://...references, preserving unrelated existing lines. - Create 1Password items from
.envfiles or save private config files as Secure Notes. - Add GitHub repository metadata to existing items for migration.
- Store valid item fields as GitHub repository secrets, guarded by item repository metadata when present.
- Store valid item fields as Cloudflare Worker secrets through Wrangler.
- Print the bundled
opzAgent Skill. - Cache item lists for 60 seconds and fall back to title contains matching when exact lookup misses.
Installation
cargo install opz
Trusted publishing
This repository is configured for crates.io trusted publishing.
Create a tag such as v2026.5.1 and push it to trigger the Publish to crates.io workflow, which mints a short-lived token through OIDC and runs cargo publish --locked.
Enable trusted publishing for the opz crate in the crates.io UI before the workflow requests tokens. The linked repository should be f4ah6o/opz.
Usage
Find Items
Search item titles by keyword:
opz find <query>
Example:
opz find github
# Output: <item-id> <vault-name> github-token
Doctor
Check 1Password CLI status and external command dependencies:
opz doctor
doctor exits non-zero when required op checks fail. Missing optional tools such as gh, wrangler, git, sh, or secretlint are reported as warnings. It also checks for plaintext .env-style credential files and, when secretlint is available, runs it against those files.
Show Item Labels
Show field labels that can be used as environment variable names:
opz show [OPTIONS] [--with-item] <ITEM>...
Options:
--vault <NAME>- Vault name (optional, searches all vaults if omitted)--with-item- Show per-item headers
Examples:
# Label names only (one per line)
opz show foo bar
# Include item header sections
opz show --with-item foo bar
Emit Agent Skill
Print the bundled Agent Skills SKILL.md for opz:
opz skills
This lets other agents and tools load the current opz usage context directly in the Agent Skills standard format.
Run Commands with Secrets
Run a command with secrets from one or more 1Password items:
opz run [OPTIONS] [--env-file <ENV>] <ITEM>... -- <COMMAND>...
opz [OPTIONS] [--env-file <ENV>] <ITEM>... -- <COMMAND>...
Options:
--vault <NAME>- Vault name (optional, searches all vaults if omitted)--env-file <ENV>- Output env file path. If omitted, no file is written.
Arguments:
<ITEM>...- One or more item titles to fetch secrets from.
When --env-file is set, the file remains after the command exits. Existing files are merged: unrelated lines stay in place, duplicate keys are overwritten, and new keys are appended. If multiple items define the same key, later items win (opz run foo bar ... prefers values from bar).
Examples:
# Recommended: run with one item and no .env file generated
opz run example-item -- your-command
# Run command with multiple items (later items win on duplicate keys)
opz run foo bar -- your-command
# Generate an env file only when another tool requires op:// references
opz run --env-file .env foo bar -- your-command
# Top-level shorthand also supports multiple items
opz --env-file .env.local foo bar -- your-command
# Quote variables so your shell leaves them for opz to expand
opz run my-service -- curl -H 'Authorization: Bearer $API_TOKEN' https://api.example.test
# Specify vault
opz run --vault Private foo bar -- your-command
Generate Env File
Generate op://... env references without running a command:
opz gen [OPTIONS] [--env-file <ENV>] <ITEM>...
Examples:
# Output sectioned env references to stdout
opz gen foo bar
# Generate .env file
opz gen --env-file .env foo bar
# Generate to custom path
opz gen --env-file .env.production foo bar
# Specify vault
opz --vault Private gen foo bar
Stdout uses per-item comment headers such as # --- item: <title> ---. File output writes the merged key list without those section comments.
Create Item from .env or Private Config
create has two modes, selected by the source file name:
opz [OPTIONS] create <ITEM> [ENV]
Arguments:
<ITEM>- New item title when[ENV]is exactly.env.[ENV]- Source file path. The default is.env.
Behavior:
- If
[ENV]is exactly.env:- Creates an
API_CREDENTIALitem. - Uses
<ITEM>as the title. - Adds each
KEY=VALUEas a custom text field namedKEY. - Records parseable git remotes in a
github_repositoriesmetadata field, oneowner/repoper line. - Supports
export KEY=..., inline comments (KEY=value # note), and#inside quotes. - Skips invalid env keys and existing
op://...values. - For duplicate keys, the last entry wins.
- Creates an
- If
[ENV]is anything other than.env:- Creates
SECURE_NOTEitem(s). - Stores the file as a fenced note body:
```<file name>\n<content>\n```. - Uses git remote repository names (
org/repo) as item titles. - If multiple remotes exist, creates one item per remote; duplicate titles get
-2,-3, and so on. - Fails if no parseable git remote is available.
- Creates
Examples:
# Create item from .env
opz create my-service
# Save private config as Secure Note (title from git remote org/repo)
opz create ignored-item app.conf
# Create item in specific vault
opz --vault Private create my-service .env
Add GitHub Repository Metadata to Existing Items
Add or update github_repositories metadata on existing 1Password items:
opz github-repo [OPTIONS] <ITEM>...
Options:
--repo <OWNER/REPO>- Repository to record. Repeat for multiple repositories. Defaults to parseable git remotes from the current repository.--dry-run- Print the metadata update without editing items.--vault <NAME>- Vault name (optional, searches all vaults if omitted)
Examples:
# Preview migration using current git remotes
opz github-repo --dry-run my-service shared-secrets
# Add current git remote repository metadata
opz github-repo my-service shared-secrets
# Add explicit repositories
opz github-repo --repo owner/repo --repo other/service my-service
Existing github_repositories entries are preserved and merged with the requested repositories.
Store GitHub Repository Secrets
Store valid item fields as GitHub repository secrets:
opz github-secret [OPTIONS] <ITEM>...
Options:
--repo <OWNER/REPO>- Target GitHub repository (defaults to the currentghrepository)--dry-run- Print the secret names that would be set without writing values--vault <NAME>- Vault name (optional, searches all vaults if omitted)
Examples:
# Preview secret names
opz github-secret --dry-run my-service
# Store secrets in the current repository
opz github-secret my-service
# Store secrets in a specific repository
opz github-secret --repo owner/repo my-service shared-secrets
github-secret uses the same valid field labels as gen and run. Duplicate names across multiple items use the later item. Secret values are resolved in memory and passed to gh secret set through stdin; values are not printed or passed as command arguments. Names starting with GITHUB_ are rejected because GitHub reserves that prefix.
If a selected 1Password item has a github_repositories field, the target repository must match one of its owner/repo entries before opz resolves or writes secret values. Multiple repositories are allowed by separating entries with newlines or commas. Items without this metadata are still allowed, but opz prints a warning because the repository guard cannot be applied.
Store Cloudflare Worker Secrets
Store valid item fields as Cloudflare Worker secrets through Wrangler:
opz cloudflare-secret [OPTIONS] <ITEM>...
Options:
--name <WORKER>- Worker name passed towrangler secret bulk --name--env <ENV>- Wrangler environment passed towrangler secret bulk --env--config <PATH>- Wrangler config path passed towrangler secret bulk --config--dry-run- Print the secret names that would be set without writing values--vault <NAME>- Vault name (optional, searches all vaults if omitted)
Examples:
# Preview secret names
opz cloudflare-secret --dry-run my-service
# Store secrets using the current Wrangler project config
opz cloudflare-secret my-service
# Store secrets for a specific Worker environment
opz cloudflare-secret --name worker-app --env production my-service shared-secrets
cloudflare-secret uses the same valid field labels as gen and run. Duplicate names across multiple items use the later item. Secret values are resolved in memory and passed to wrangler secret bulk through stdin as JSON; values are not printed or passed as command arguments.
How It Works
- Fetches the item list from 1Password and caches that metadata for 60 seconds.
- Finds one matching title. Exact match is tried first; title contains matching is used as a fallback.
- Fetches the matched item and builds
op://<vault_id>/<item_id>/<field>references for fields with valid env labels. - Writes an env file when requested, merging with any existing file. The recommended path is file-free
opz run; env files are for tools that requireop://references. - Resolves secrets with
op run --env-file <temp> -- sh -c 'env -0', falling back toop readper reference if batch resolution fails. - Runs the command with resolved values in the environment.
$VARand${VAR}in command arguments are expanded only for variables resolved from the selected items.
gen stops after writing references. show fetches items and prints valid labels without resolving secret values.
op Command Usage
For security transparency, here's how opz uses the op CLI:
sequenceDiagram
participant opz
participant op as op CLI
Note over opz: User runs: opz example-item -- claude "hello"
opz->>op: op item list --format json
op-->>opz: [{id, title, vault}, ...]
Note over opz: Match "example-item" → get item ID
opz->>op: op item get <id> --format json
op-->>opz: {fields: [{label, value}, ...]}
Note over opz: Resolve secret values<br/>(inject as env vars)
Note over opz: Optional: write .env if specified
opz->>op: sh -c "claude \"hello\""
Note over opz: Execute with secrets in environment
op-->>opz: Exit status
Security: opz delegates secret access and authentication to the op CLI. The 60-second cache stores item-list metadata only, not secret values.
Tracing (OpenTelemetry + Jaeger)
opz can emit OTLP traces, but it is disabled by default. If OTEL_EXPORTER_OTLP_ENDPOINT is not set, tracing is a no-op.
Local setup
just jaeger-up
just trace-run item=<your-item-title>
just trace-ui
E2E trace on Jaeger
If you want to inspect traces generated by tests/e2e_real_op.rs:
just jaeger-up
just e2e-trace
just trace-ui
In Jaeger Search, select service opz-e2e.
just e2e-trace automatically sets OPZ_GIT_COMMIT=$(git rev-parse --short=12 HEAD).
Compare traces by ref or version
Generate traces on each target commit/tag (or release version), then compare:
just trace-report <ref-or-version>
just trace-compare <base-ref-or-version> <head-ref-or-version>
<ref-or-version> accepts commit hash, git tag (for example v2026.5.1), or service.version (for example 2026.5.1).
Both commands print markdown tables (duration and top child span) for easy copy into PRs.
For less noisy comparisons, aggregate multiple runs and ignore failed traces:
just trace-report-samples <ref-or-version> samples=5 status=ok
just trace-compare-samples <base-ref-or-version> <head-ref-or-version> samples=5 status=ok
samples uses latest N traces per operation and reports median/average.
status can be all, ok, or error.
Then open Jaeger Search and select service opz (or your OTEL_SERVICE_NAME) to inspect spans such as:
cli.<command>(root)parse_argsload_configload_inputsmain_operationwrite_outputs
Environment variables
OTEL_EXPORTER_OTLP_ENDPOINT- Enables OTLP export when set (example:http://localhost:4317)OTEL_SERVICE_NAME- Optional service name override (default:opz)OTEL_TRACES_SAMPLER- Optional sampler setting (always_on,traceidratio, etc.)OTEL_TRACES_SAMPLER_ARG- Optional sampler parameter (for ratio-based samplers)OPZ_TRACE_CAPTURE_ARGS-1to include sanitizedcli.argsin trace attributes (default: disabled)OPZ_GIT_COMMIT- Optional override for trace resource attributegit.commit(default:git rev-parse --short=12 HEAD)
Requirements
- 1Password CLI (
op) installed and authenticated - Optional: GitHub CLI (
gh) forgithub-secret - Optional: Wrangler (
wrangler) forcloudflare-secret - Optional: Git (
git) for private configcreate
E2E Test
Real 1Password e2e test is available in tests/e2e_real_op.rs.
It is gated for safety and runs only when OPZ_E2E=1 is set:
OPZ_E2E=1 cargo test --test e2e_real_op -- --nocapture
Or use just:
just e2e
Dependencies
~19–31MB
~505K SLoC