
1password
- 156 installs
- 6 repo stars
- Updated July 22, 2026
- julianobarbosa/claude-code-skills
Retrieve and inject API keys, tokens, and credentials from 1Password vaults during development workflows without hardcoding secrets into repos or local env files.
About
Documents patterns for using 1Password to supply development and CI secrets—vault references, CLI invocations, and safe injection into apps and agents—reducing credential leakage while integrating third-party APIs during build.
- vault-aware secret fetch
- CLI and SDK patterns
- env injection workflows
- least-privilege access
- no plaintext in git
1password by the numbers
- 156 all-time installs (skills.sh)
- +1 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #876 of 2,203 Security skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/julianobarbosa/claude-code-skills --skill 1passwordAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 156 |
|---|---|
| repo stars | ★ 6 |
| Last updated | July 22, 2026 |
| Repository | julianobarbosa/claude-code-skills ↗ |
What it does
Retrieve and inject API keys, tokens, and credentials from 1Password vaults during development workflows without hardcoding secrets into repos or local env files.
Files
1Password
Overview
This skill provides comprehensive guidance for working with 1Password's secrets management ecosystem. It covers the op CLI for local development, service accounts for automation, Developer Environments for project secrets, and Kubernetes integrations including the native 1Password Operator and External Secrets Operator.
Quick Reference
Command Structure
1Password CLI uses a noun-verb structure: op <noun> <verb> [flags]
# Authentication
op signin # Sign in to account
op signout # Sign out
op whoami # Show signed-in account info
# Secret retrieval
op read "op://vault/item/field" # Read single secret
op run -- <command> # Inject secrets as env vars
op inject -i template.env -o .env # Inject secrets into file
# Item management
op item list # List all items
op item get <item> # Get item details
op item create --category login # Create new item
op item edit <item> field=value # Edit item
op item delete <item> # Delete item
# Vault management
op vault list # List vaults
op vault get <vault> # Get vault info
op vault create <name> # Create vault
# Document management
op document list # List documents
op document get <document> # Download document
op document create <file> --vault <vault> # Upload documentWorkflow Decision Tree
What do you need to do?
├── Retrieve a secret for local development?
│ └── Use: op read, op run, or op inject
├── Manage project environment variables?
│ └── See: Developer Environments (below)
├── Manage items/vaults in 1Password?
│ └── Use: op item, op vault, op document commands
├── Automate secrets in CI/CD?
│ └── Use: Service Accounts with OP_SERVICE_ACCOUNT_TOKEN
├── Sync secrets to Kubernetes?
│ ├── Using External Secrets Operator?
│ │ └── See: External Secrets Operator Integration
│ └── Using native 1Password Operator?
│ └── See: 1Password Kubernetes Operator
└── Configure shell plugins for CLI tools?
└── Use: op plugin commandsDeveloper Environments
Developer Environments provide a dedicated location to store, organize, and manage project secrets as environment variables. CLI tools are available in both TypeScript/Bun and Python SDK variants.
Feature Overview
| Feature | GUI | TypeScript CLI | Python SDK CLI |
|---|---|---|---|
| Create environment | Yes | bun run create | uv run op-env-create |
| Update environment | Yes | bun run update | uv run op-env-update |
| Delete environment | Yes | bun run delete | uv run op-env-delete |
| Show environment | Yes | bun run show | uv run op-env-show |
| List environments | Yes | bun run list | uv run op-env-list |
| Export to .env | Yes | bun run export | uv run op-env-export |
| Mount .env file | Yes (beta) | No | No |
CLI Tools Setup (TypeScript)
Tools are written in TypeScript and require Bun runtime:
# Navigate to tools directory
cd tools
# Run any tool with bun
bun run src/op-env-create.ts --help
bun run src/op-env-list.ts --help
# Or use npm scripts
bun run create -- --help
bun run list -- --helpCLI Tools Setup (Python SDK)
Python tools use the official onepassword-sdk package and require uv:
# Navigate to tools-python directory
cd tools-python
# Install dependencies
uv sync
# Run any tool
uv run op-env-create --help
uv run op-env-list --helpRequirements: Python 3.9+, OP_SERVICE_ACCOUNT_TOKEN environment variable.
When to Use SDK vs CLI
| Use Case | Recommended | Why |
|---|---|---|
| Python applications (FastAPI, Django) | Python SDK | Native async, no subprocess overhead |
| Shell scripts, CI/CD pipelines | TypeScript CLI or op CLI | Direct CLI integration |
| Batch secret resolution | Python SDK | resolve_all() for efficiency |
| Tag-based filtering | TypeScript CLI | SDK lacks tag filter support |
| Interactive local development | Either | Both have identical interfaces |
SecretsManager (Python SDK)
For Python applications that need runtime secret resolution:
from op_env.secrets_manager import SecretsManager
async def main():
sm = await SecretsManager.create()
# Single secret (with caching)
api_key = await sm.get("op://Production/API/key")
# Batch resolve
secrets = await sm.get_many([
"op://Production/DB/password",
"op://Production/DB/host",
])
# Load all vars from an environment item
env = await sm.resolve_environment("my-app-prod", "Production")See references/python-sdk.md for full SDK reference and integration patterns.
Environment Workflow
1. Create Environment
# From inline variables
bun run src/op-env-create.ts my-app-dev Personal \
API_KEY=secret \
DB_HOST=localhost \
DB_PORT=5432
# From .env file
bun run src/op-env-create.ts my-app-prod Production --from-file .env.prod
# Combine file + inline (inline overrides file)
bun run src/op-env-create.ts azure-config Shared --from-file .env EXTRA_KEY=value
# With custom tags
bun run src/op-env-create.ts secrets DevOps --tags "env,production,api" KEY=value2. List Environments
# List all environments (tagged with 'environment')
bun run src/op-env-list.ts
# Filter by vault
bun run src/op-env-list.ts --vault Personal
# Filter by tags
bun run src/op-env-list.ts --tags "production"
# JSON output
bun run src/op-env-list.ts --json3. Show Environment Details
# Show with masked values (default)
bun run src/op-env-show.ts my-app-dev Personal
# Show with revealed values
bun run src/op-env-show.ts my-app-dev Personal --reveal
# JSON output
bun run src/op-env-show.ts my-app-dev Personal --json
# Show only variable names
bun run src/op-env-show.ts my-app-dev Personal --keys4. Update Environment
# Update/add single variable
bun run src/op-env-update.ts my-app-dev Personal API_KEY=new-key
# Merge from .env file
bun run src/op-env-update.ts my-app-dev Personal --from-file .env.local
# Remove variables
bun run src/op-env-update.ts my-app-dev Personal --remove OLD_KEY,DEPRECATED
# Update and remove in one command
bun run src/op-env-update.ts my-app-dev Personal NEW_KEY=value --remove OLD_KEY5. Export Environment
# Export to .env file (standard format)
bun run src/op-env-export.ts my-app-dev Personal > .env
# Docker-compatible format (quoted values)
bun run src/op-env-export.ts my-app-dev Personal --format docker > .env
# op:// references template (for op run/inject)
bun run src/op-env-export.ts my-app-dev Personal --format op-refs > .env.tpl
# JSON format
bun run src/op-env-export.ts my-app-dev Personal --format json
# Add prefix to all variables
bun run src/op-env-export.ts azure-config Shared --prefix AZURE_ > .env6. Delete Environment
# Interactive deletion (asks for confirmation)
bun run src/op-env-delete.ts my-app-dev Personal
# Force delete without confirmation
bun run src/op-env-delete.ts my-app-dev Personal --force
# Archive instead of permanent delete
bun run src/op-env-delete.ts my-app-dev Personal --archiveEnvironment Secret Reference
Access individual variables using the secret reference format:
op://<vault>/<environment>/variables/<key>Example:
# Read single variable
op read "op://Personal/my-app-dev/variables/API_KEY"
# Use in template file (.env.tpl)
API_KEY=op://Personal/my-app-dev/variables/API_KEY
DB_HOST=op://Personal/my-app-dev/variables/DB_HOSTIntegration Patterns
With op run (recommended)
# 1. Export environment as op:// template
bun run src/op-env-export.ts my-app-dev Personal --format op-refs > .env.tpl
# 2. Run command with injected secrets
op run --env-file .env.tpl -- ./deploy.sh
op run --env-file .env.tpl -- docker compose up
op run --env-file .env.tpl -- npm start
op run --env-file .env.tpl -- python app.pyWith op inject
# 1. Create template with op:// references
bun run src/op-env-export.ts my-app-dev Personal --format op-refs > config.tpl
# 2. Inject secrets into file
op inject -i config.tpl -o .env
# 3. Use the generated .env file
source .env && ./appWith Docker Compose
# 1. Export environment
bun run src/op-env-export.ts my-app-dev Personal --format op-refs > .env.tpl
# 2. Run docker compose with secrets
op run --env-file .env.tpl -- docker compose up -dIn CI/CD (GitHub Actions)
name: Deploy
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install 1Password CLI
uses: 1password/install-cli-action@v1
- name: Load secrets
uses: 1password/load-secrets-action@v2
with:
export-env: true
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
API_KEY: op://CI-CD/my-app-prod/variables/API_KEY
DB_PASSWORD: op://CI-CD/my-app-prod/variables/DB_PASSWORD
- name: Deploy
run: ./deploy.shCurrent Environments (Barbosa Account)
| Environment | Vault | Description |
|---|---|---|
| hypera-azure-rg-hypera-cafehyna-web-dev | - | Azure RG - Cafehyna Web Dev |
| hypera-azure-devops-team-az-cli-pim | - | Azure DevOps Team - CLI PIM |
| devops-team-pim | - | DevOps Team PIM credentials |
| hypera-github-python-devops | - | GitHub - Python DevOps |
| hypera-azure-rg-hypera-cafehyna-web | - | Azure RG - Cafehyna Web Prod |
| repos-github-zsh | - | GitHub - ZSH repository |
| hypera | - | General Hypera infrastructure |
| Azure OpenAI-finops | - | Azure OpenAI FinOps config |
See references/environments/inventory.md for detailed documentation.
Secret Retrieval
Secret Reference Format
The standard format for referencing secrets:
op://<vault>/<item>/<field>Examples:
op://Development/AWS/access_key_idop://Production/Database/passwordop://Shared/API Keys/github_token
Reading Secrets Directly
# Read a specific field
op read "op://Development/AWS/access_key_id"
# Read with JSON output
op item get "AWS" --vault Development --format json
# Read specific field from item
op item get "AWS" --vault Development --fields access_key_idInjecting Secrets into Commands
The op run command injects secrets as environment variables:
# Run command with secrets
op run --env-file=.env.tpl -- ./deploy.sh
# Example .env.tpl file:
# AWS_ACCESS_KEY_ID=op://Development/AWS/access_key_id
# AWS_SECRET_ACCESS_KEY=op://Development/AWS/secret_access_keyInjecting Secrets into Files
The op inject command replaces secret references in template files:
# Inject secrets from template to output file
op inject -i config.tpl.yaml -o config.yaml
# Example config.tpl.yaml:
# database:
# host: localhost
# password: op://Production/Database/passwordItem Management
Creating Items
# Create a login item
op item create --category login \
--title "My Service" \
--vault Development \
username=admin \
password=secretpassword
# Create with generated password
op item create --category login \
--title "New Account" \
--generate-password
# Create from JSON template
op item create --template item.jsonItem Template (JSON)
{
"title": "my-service-credentials",
"vault": {"id": "vault-uuid-or-name"},
"category": "LOGIN",
"fields": [
{"label": "username", "value": "admin", "type": "STRING"},
{"label": "password", "value": "secret", "type": "CONCEALED"},
{"label": "api_key", "value": "key123", "type": "CONCEALED"}
]
}Editing Items
# Edit a field
op item edit "My Service" password=newpassword
# Add a new field
op item edit "My Service" api_key=newkey
# Edit with specific vault
op item edit "My Service" --vault Development password=newpasswordService Accounts
Service accounts enable automation without personal credentials.
Prerequisites
- 1Password CLI version 2.18.0 or later
- Active 1Password subscription
- Admin permissions to create service accounts
Creating Service Accounts
Via CLI:
# Create with read-only access
op service-account create "CI/CD Pipeline" \
--vault Production:read_items
# Create with write access
op service-account create "Deployment Bot" \
--vault Production:read_items,write_items
# Create with vault creation permission
op service-account create "Provisioning Bot" \
--vault Production:read_items,write_items \
--can-create-vaultsUsing Service Accounts
Export the service account token:
export OP_SERVICE_ACCOUNT_TOKEN="ops_..."Then use normal CLI commands - they automatically authenticate with the service account.
Service Account Limitations
- Cannot access Personal, Private, Employee, or default Shared vaults
- Permissions cannot be modified after creation
- Limited to 100 service accounts per account
- Subject to rate limits
CI/CD Integration
GitHub Actions
name: Deploy
on: [push]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install 1Password CLI
uses: 1password/install-cli-action@v1
- name: Load secrets
uses: 1password/load-secrets-action@v2
with:
export-env: true
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
AWS_ACCESS_KEY_ID: op://CI-CD/AWS/access_key_id
AWS_SECRET_ACCESS_KEY: op://CI-CD/AWS/secret_access_key
- name: Deploy
run: ./deploy.shGitLab CI
deploy:
image: 1password/op:2
variables:
OP_SERVICE_ACCOUNT_TOKEN: $OP_SERVICE_ACCOUNT_TOKEN
script:
- export AWS_ACCESS_KEY_ID=$(op read "op://CI-CD/AWS/access_key_id")
- export AWS_SECRET_ACCESS_KEY=$(op read "op://CI-CD/AWS/secret_access_key")
- ./deploy.shCircleCI
version: 2.1
orbs:
onepassword: onepassword/secrets@1
jobs:
deploy:
docker:
- image: cimg/base:stable
steps:
- checkout
- onepassword/exec:
command: ./deploy.sh
env:
AWS_ACCESS_KEY_ID: op://CI-CD/AWS/access_key_id
AWS_SECRET_ACCESS_KEY: op://CI-CD/AWS/secret_access_keyExternal Secrets Operator Integration
External Secrets Operator (ESO) syncs secrets from 1Password to Kubernetes.
Prerequisites
1. 1Password Connect Server (v1.5.6+) 2. Credentials file (1password-credentials.json) 3. Access token for authentication 4. External Secrets Operator installed in cluster
Connect Server Setup
# Create automation environment and get credentials
# This generates 1password-credentials.json and an access token
# Create Kubernetes secret for Connect Server credentials
kubectl create secret generic onepassword-credentials \
--from-file=1password-credentials.json
# Create secret for access token
kubectl create secret generic onepassword-token \
--from-literal=token=your-access-tokenDeploy Connect Server
apiVersion: apps/v1
kind: Deployment
metadata:
name: onepassword-connect
spec:
replicas: 1
selector:
matchLabels:
app: onepassword-connect
template:
metadata:
labels:
app: onepassword-connect
spec:
containers:
- name: connect-api
image: 1password/connect-api:latest
ports:
- containerPort: 8080
volumeMounts:
- name: credentials
mountPath: /home/opuser/.op/1password-credentials.json
subPath: 1password-credentials.json
volumes:
- name: credentials
secret:
secretName: onepassword-credentials
---
apiVersion: v1
kind: Service
metadata:
name: onepassword-connect
spec:
selector:
app: onepassword-connect
ports:
- port: 8080
targetPort: 8080ClusterSecretStore Configuration
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: onepassword
spec:
provider:
onepassword:
connectHost: http://onepassword-connect:8080
vaults:
production: 1
staging: 2
auth:
secretRef:
connectTokenSecretRef:
name: onepassword-token
namespace: external-secrets
key: tokenExternalSecret Examples
Basic secret retrieval:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: database-credentials
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: onepassword
target:
name: database-credentials
creationPolicy: Owner
data:
- secretKey: username
remoteRef:
key: Database # Item title in 1Password
property: username # Field label
- secretKey: password
remoteRef:
key: Database
property: passwordUsing dataFrom with regex:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: env-config
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: onepassword
target:
name: app-env
dataFrom:
- find:
path: app-config # Item title
name:
regexp: "^[A-Z_]+$" # Match all uppercase env varsPushSecret (Kubernetes to 1Password)
apiVersion: external-secrets.io/v1alpha1
kind: PushSecret
metadata:
name: push-generated-secret
spec:
refreshInterval: 1h
secretStoreRefs:
- name: onepassword
kind: ClusterSecretStore
selector:
secret:
name: generated-credentials
data:
- match:
secretKey: api-key
remoteRef:
remoteKey: generated-api-key
property: password
metadata:
apiVersion: kubernetes.external-secrets.io/v1alpha1
kind: PushSecretMetadata
spec:
vault: production
tags:
- generated
- kubernetes1Password Kubernetes Operator
The native 1Password Operator provides direct integration without External Secrets Operator.
Installation via Helm
helm repo add 1password https://1password.github.io/connect-helm-charts
helm install connect 1password/connect \
--set-file connect.credentials=1password-credentials.json \
--set operator.create=true \
--set operator.token.value=your-access-tokenOnePasswordItem CRD
apiVersion: onepassword.com/v1
kind: OnePasswordItem
metadata:
name: database-secret
spec:
itemPath: "vaults/Production/items/Database"This creates a Kubernetes Secret named database-secret with all fields from the 1Password item.
Auto-Restart Configuration
Enable automatic deployment restarts when secrets change:
# Operator-level (environment variable)
AUTO_RESTART=true
# Namespace-level (annotation)
apiVersion: v1
kind: Namespace
metadata:
name: production
annotations:
operator.1password.io/auto-restart: "true"
# Deployment-level (annotation)
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
operator.1password.io/auto-restart: "true"Shell Plugins
Shell plugins enable automatic authentication for third-party CLIs.
Available Plugins
# List available plugins
op plugin list
# Common plugins: aws, gh, stripe, vercel, fly, etc.Plugin Setup
# Initialize AWS plugin
op plugin init aws
# This configures shell aliases to use 1Password for AWS credentials
# Add to your shell profile as instructedGit Workflow with 1Password
Use 1Password to manage GitHub authentication for git operations (push, pull, clone).
Quick Setup
Run the setup script to configure everything:
./scripts/setup-gh-plugin.shManual Setup
Step 1: Initialize the gh plugin
# Sign in to 1Password
op signin
# Initialize gh plugin (interactive - select your GitHub token)
op plugin init ghStep 2: Configure git credential helper
# Remove any broken credential helpers
git config --global --unset-all credential.https://github.com.helper 2>/dev/null
# Set gh as the credential helper for GitHub
git config --global credential.https://github.com.helper '!/opt/homebrew/bin/gh auth git-credential'
git config --global credential.https://gist.github.com.helper '!/opt/homebrew/bin/gh auth git-credential'Step 3: Add shell integration
Add to your ~/.zshrc or ~/.bashrc:
# 1Password CLI plugins
source ~/.config/op/plugins.shHow It Works
┌─────────────────────────────────────────────────────────────────┐
│ Git Push Workflow │
├─────────────────────────────────────────────────────────────────┤
│ │
│ git push │
│ │ │
│ ▼ │
│ Git credential helper │
│ │ │
│ ▼ │
│ gh auth git-credential │
│ │ │
│ ▼ │
│ 1Password plugin (via op wrapper) │
│ │ │
│ ▼ │
│ 1Password (biometric/password unlock) │
│ │ │
│ ▼ │
│ Token retrieved and passed to git │
│ │ │
│ ▼ │
│ Push completes successfully │
│ │
└─────────────────────────────────────────────────────────────────┘Multiple GitHub Accounts
If you work with multiple GitHub accounts, you can configure per-repo credentials:
# For a specific repo, use a different 1Password item
cd /path/to/work-repo
git config credential.https://github.com.helper '!/opt/homebrew/bin/gh auth git-credential'
# Or use includeIf in ~/.gitconfig for path-based selection
[includeIf "gitdir:~/work/"]
path = ~/.gitconfig-workFixing Common Issues
"Item not found in vault" error
This means the 1Password plugin is pointing to a deleted token:
# Remove the broken plugin configuration
rm ~/.config/op/plugins/used_items/gh.json
# Re-initialize
op plugin init ghgh aliased to op plugin run
If gh is aliased to run through 1Password but failing:
# Check the alias
which gh # Shows: gh: aliased to op plugin run -- gh
# Run gh directly to bypass the alias
/opt/homebrew/bin/gh auth statusGit prompting for username/password
Verify the credential helper is configured:
git config --list | grep credentialShould show:
credential.https://github.com.helper=!/opt/homebrew/bin/gh auth git-credentialTroubleshooting
Common Issues
Authentication fails:
# Check current session
op whoami
# Sign in again
op signin
# For service accounts, verify token
echo $OP_SERVICE_ACCOUNT_TOKEN | head -c 10Item not found:
# List items in vault to verify name
op item list --vault "Vault Name"
# Use item ID instead of name for reliability
op item get --vault Development dh7fjsh3kd8fjsPermission denied in CI/CD:
# Verify service account has access to vault
op vault list # Should show accessible vaults
# Check rate limits
op service-account ratelimitExternal Secrets not syncing:
# Check ExternalSecret status
kubectl describe externalsecret <name>
# Check Connect Server logs
kubectl logs -l app=onepassword-connect
# Verify SecretStore connection
kubectl describe secretstore <name>Best Practices
1. Use secret references (op://) instead of hardcoding vault/item names in scripts 2. Prefer service accounts over personal accounts for automation 3. Scope permissions minimally - grant only necessary vault access 4. Use item IDs in scripts for stability (names can change) 5. Rotate service account tokens when sign-in addresses change 6. Enable auto-restart in Kubernetes for seamless secret rotation 7. Use separate vaults per environment (dev, staging, prod) 8. Tag items for organization and filtering
Resources
References
references/cli-commands.md- Complete CLI command referencereferences/kubernetes-examples.md- Kubernetes manifest examplesreferences/python-sdk.md- Python SDK reference and integration guidereferences/environments/README.md- Developer Environments guidereferences/environments/inventory.md- Current environments inventory
Tools
Environment management CLI tools in TypeScript and Python:
| Operation | TypeScript (tools/) | Python (tools-python/) |
|---|---|---|
| Create | bun run create | uv run op-env-create |
| Update | bun run update | uv run op-env-update |
| Delete | bun run delete | uv run op-env-delete |
| Show | bun run show | uv run op-env-show |
| List | bun run list | uv run op-env-list |
| Export | bun run export | uv run op-env-export |
TypeScript requirements: Bun runtime Python requirements: Python 3.9+, uv, OP_SERVICE_ACCOUNT_TOKEN
# TypeScript tools
cd tools && bun run src/op-env-list.ts --help
# Python SDK tools
cd tools-python && uv sync && uv run op-env-list --helpTemplates
Environment and integration templates (in templates/):
| Template | Description |
|---|---|
env.template | Standard .env file template |
env-op-refs.template | Template with op:// references |
github-actions-env.yaml | GitHub Actions workflow example |
docker-compose-env.yaml | Docker Compose with secrets injection |
Scripts
scripts/setup-gh-plugin.sh- Setup GitHub CLI with 1Password integrationscripts/setup-service-account.sh- Create and configure a service accountscripts/sync-check.sh- Verify External Secrets synchronization
External Documentation
- 1Password CLI Documentation
- Service Accounts Guide
- Developer Environments
- Local .env Files
- Kubernetes Operator
- External Secrets Provider
---
Gotchas
- Service Account vs Connect Server vs CLI auth modes differ in rate limits, vault visibility, and cred shape — a script using
opinteractively won't necessarily work as a service-account token. - `op inject` evaluates `op://...` refs at render time — templates checked into git are safe; rendered output never goes near git.
- CLI biometric prompt unless `OP_DEVICE` is set — CI containers without that env var fail silently as if there were no secrets.
- Shell plugin (`op plugin init`) sources at shell start — plugin updates don't apply until you open a new shell.
- `op read op://vault/item/field` returns the FIRST match across duplicates — items with the same field name resolve by lexicographic order, not creation date.
1Password CLI Command Reference
Complete reference for all op CLI commands and their options.
Authentication Commands
op signin
Sign in to a 1Password account.
op signin [account-shorthand]
op signin --raw # Output session token only
op signin --force # Force re-authenticationop signout
Sign out of a 1Password account.
op signout # Sign out of current account
op signout --all # Sign out of all accounts
op signout --forget # Remove account from local configop whoami
Display information about the signed-in account.
op whoami # Show account info
op whoami --format json # JSON outputSecret Operations
op read
Read a secret using a secret reference.
op read "op://vault/item/field"
op read "op://vault/item/field" --no-newline # No trailing newline
op read "op://vault/item/field" --force # Skip confirmation promptsop run
Run a command with secrets injected as environment variables.
op run -- command [args]
op run --env-file .env.tpl -- command
op run --no-masking -- command # Don't mask secrets in outputop inject
Inject secrets into a template file.
op inject -i template.env -o output.env
op inject -i template.yaml # Output to stdout
op inject --in-file template --out-file output
op inject --force # Overwrite existing outputItem Commands
op item list
List items in vaults.
op item list
op item list --vault "Vault Name"
op item list --categories login,password
op item list --tags "production,api"
op item list --format json
op item list --favorite # Only favorites
op item list --include-archive # Include archived itemsop item get
Get details of a specific item.
op item get "Item Name"
op item get "Item Name" --vault "Vault Name"
op item get "Item Name" --fields label=password
op item get "Item Name" --fields password,username
op item get "Item Name" --format json
op item get "Item Name" --reveal # Show concealed fields
op item get itemid # By ID (more reliable)op item create
Create a new item.
# Basic login
op item create --category login --title "My Service" \
--vault "Development" \
username=admin password=secret
# With generated password
op item create --category login --title "New Account" \
--generate-password
# With specific password recipe
op item create --category login --title "Secure Service" \
--generate-password="letters,digits,symbols,32"
# From template file
op item create --template item.json
# Categories: login, password, identity, credit_card, secure_note,
# document, bank_account, database, email_account,
# wireless_router, server, software_license, api_credentialop item edit
Edit an existing item.
op item edit "Item Name" field=newvalue
op item edit "Item Name" --vault "Vault" password=newsecret
op item edit "Item Name" "section.field=value"
op item edit "Item Name" --title "New Title"
op item edit "Item Name" --generate-password
op item edit "Item Name" --favorite
op item edit "Item Name" --tags "tag1,tag2"op item delete
Delete an item.
op item delete "Item Name"
op item delete "Item Name" --vault "Vault"
op item delete "Item Name" --archive # Archive instead of deleteop item share
Share an item.
op item share "Item Name" # Generate share link
op item share "Item Name" --expiry 1h # 1 hour expiry
op item share "Item Name" --expiry 7d # 7 days expiry
op item share "Item Name" --view-once # Single view only
op item share "Item Name" --emails user@example.comop item move
Move an item between vaults.
op item move "Item Name" --current-vault "Source" --destination-vault "Dest"Vault Commands
op vault list
List accessible vaults.
op vault list
op vault list --format json
op vault list --group "Group Name"op vault get
Get details of a vault.
op vault get "Vault Name"
op vault get "Vault Name" --format jsonop vault create
Create a new vault.
op vault create "New Vault"
op vault create "New Vault" --description "Description"
op vault create "New Vault" --icon "airplane"
op vault create "New Vault" --allow-admins-to-manage falseop vault edit
Edit a vault.
op vault edit "Vault Name" --name "New Name"
op vault edit "Vault Name" --description "New description"
op vault edit "Vault Name" --icon "key"op vault delete
Delete a vault.
op vault delete "Vault Name"Document Commands
op document list
List documents.
op document list
op document list --vault "Vault Name"
op document list --format jsonop document get
Download a document.
op document get "Document Name" # To stdout
op document get "Document Name" --out-file path/to/file
op document get "Document Name" --vault "Vault"op document create
Upload a document.
op document create path/to/file --vault "Vault"
op document create path/to/file --title "Custom Title" --vault "Vault"
op document create path/to/file --tags "tag1,tag2"op document edit
Replace a document.
op document edit "Document Name" --vault "Vault" path/to/newfile
op document edit "Document Name" --title "New Title"op document delete
Delete a document.
op document delete "Document Name"
op document delete "Document Name" --vault "Vault"
op document delete "Document Name" --archiveAccount Management
op account list
List configured accounts.
op account list
op account list --format jsonop account get
Get account details.
op account get
op account get --format jsonop account add
Add an account.
op account add --address example.1password.com
op account add --address example.1password.com --email user@example.comop account forget
Remove an account from local configuration.
op account forget exampleService Account Commands
op service-account create
Create a service account.
# Read-only access
op service-account create "CI Pipeline" \
--vault Production:read_items
# Read and write access
op service-account create "Deploy Bot" \
--vault Production:read_items,write_items \
--vault Staging:read_items,write_items
# With vault creation permission
op service-account create "Provisioner" \
--vault Production:read_items,write_items \
--can-create-vaults
# Permissions: read_items, write_items, share_itemsop service-account ratelimit
Check service account rate limits.
op service-account ratelimitUser Commands
op user list
List users in account.
op user list
op user list --group "Group Name"
op user list --vault "Vault Name"
op user list --format jsonop user get
Get user details.
op user get user@example.com
op user get --meop user provision
Provision a new user.
op user provision --email user@example.com --name "User Name"op user edit
Edit a user.
op user edit user@example.com --name "New Name"
op user edit user@example.com --travel-mode onop user delete
Delete/suspend a user.
op user delete user@example.com
op user suspend user@example.comGroup Commands
op group list
List groups.
op group list
op group list --vault "Vault Name"
op group list --format jsonop group get
Get group details.
op group get "Group Name"
op group get "Group Name" --format jsonop group create
Create a group.
op group create "New Group"
op group create "New Group" --description "Description"op group edit
Edit a group.
op group edit "Group Name" --name "New Name"
op group edit "Group Name" --description "New description"op group delete
Delete a group.
op group delete "Group Name"op group user
Manage group membership.
op group user grant --group "Group" --user user@example.com
op group user revoke --group "Group" --user user@example.com
op group user list --group "Group"Connect Server Commands
op connect server list
List Connect servers.
op connect server listop connect server get
Get Connect server details.
op connect server get "Server Name"op connect server create
Create a Connect server.
op connect server create "Server Name" --vault "Vault1" --vault "Vault2"op connect server edit
Edit a Connect server.
op connect server edit "Server Name" --name "New Name"op connect server delete
Delete a Connect server.
op connect server delete "Server Name"op connect token create
Create a Connect token.
op connect token create "Token Name" --server "Server Name" --vault "Vault"op connect token list
List Connect tokens.
op connect token list --server "Server Name"op connect token delete
Delete a Connect token.
op connect token delete "Token Name" --server "Server Name"Shell Plugin Commands
op plugin list
List available plugins.
op plugin listop plugin init
Initialize a plugin.
op plugin init aws
op plugin init gh
op plugin init stripeop plugin run
Run a command with plugin credentials.
op plugin run -- aws s3 lsUtility Commands
op completion
Generate shell completion scripts.
op completion bash
op completion zsh
op completion fish
op completion powershellop update
Check for and apply updates.
op update
op update --check # Check only, don't installop --version
Display version information.
op --versionGlobal Flags
These flags work with most commands:
--account # Specify account shorthand
--cache # Enable/disable caching
--config # Config directory path
--debug # Enable debug output
--encoding # Output encoding (utf-8, shift_jis)
--format # Output format (json, human-readable)
--iso-timestamps # Use ISO 8601 timestamps
--no-color # Disable color output
--session # Session token to useEnvironment Variables
OP_SERVICE_ACCOUNT_TOKEN # Service account authentication
OP_CONNECT_TOKEN # Connect server token
OP_CONNECT_HOST # Connect server URL
OP_SESSION_* # Session tokens per account
OP_BIOMETRIC_UNLOCK_ENABLED # Enable biometric unlock
OP_DEVICE # Device UUID
OP_CONFIG_DIR # Config directory location
OP_CACHE_DIR # Cache directory location
OP_LOG_LEVEL # Logging level (debug, info, warn, error)Secret Reference Format
The canonical format for referencing secrets:
op://<vault>/<item>[/<section>]/<field>Components:
vault- Vault name or IDitem- Item name or IDsection- Optional section namefield- Field label or ID
Examples:
op://Development/AWS/access_key_id
op://Production/Database/credentials/password
op://Shared/API Keys/github_token
op://vault-uuid/item-uuid/field-uuidItem Categories
Available categories for op item create --category:
| Category | Description |
|---|---|
login | Website login credentials |
password | Standalone password |
identity | Personal identity information |
credit_card | Credit/debit card |
secure_note | Encrypted text note |
document | File attachment |
bank_account | Bank account details |
database | Database connection |
email_account | Email credentials |
wireless_router | WiFi credentials |
server | Server/SSH credentials |
software_license | License keys |
api_credential | API key/token |
ssh_key | SSH key pair |
medical_record | Medical information |
passport | Passport details |
driver_license | Driver's license |
outdoor_license | Hunting/fishing license |
membership | Membership card |
reward_program | Loyalty program |
social_security_number | SSN |
Field Types
Available field types when creating items:
| Type | Description |
|---|---|
STRING | Plain text |
CONCEALED | Hidden/password field |
EMAIL | Email address |
URL | Web URL |
DATE | Date value |
MONTH_YEAR | Month/year (cards) |
PHONE | Phone number |
ADDRESS | Physical address |
TOTP | One-time password seed |
REFERENCE | Reference to another item |
FILE | File attachment |
1Password Developer Environments Inventory
Overview
This document tracks all Developer Environments configured in 1Password under the Barbosa account.
Account: Barbosa Last Updated: 2026-02-19 Vault: hypera (service account access)
Current Environments
| # | Environment Name | Description | Vault | Status | Created |
|---|---|---|---|---|---|
| 1 | Azure OpenAI-finops | Azure OpenAI FinOps configuration | hypera | Active | 2026-02-19 |
| 2 | devops-team-pim | DevOps Team PIM access credentials | hypera | Active | 2026-02-19 |
| 3 | .dotfiles | Dotfiles environment variables | hypera | Active | 2026-02-19 |
| 4 | hypera-github-python-devops | GitHub credentials for Python DevOps projects | hypera | Active | 2026-02-19 |
| 5 | hypera-azure-rg-hypera-cafehyna-web | Azure Resource Group - Cafehyna Web (Production) | hypera | Active | 2026-02-19 |
| 6 | devops-team-azure-quota-automation | DevOps team Azure quota automation credentials | hypera | Active | 2026-02-19 |
| 7 | hypera-azure-rg-hypera-cafehyna-web-dev | Azure Resource Group - Cafehyna Web Dev | hypera | Active | 2026-02-19 |
| 8 | hypera | General Hypera environment credentials | hypera | Active | 2026-02-19 |
| 9 | rg-hypera-aks-python-infra-dev | AKS Python infrastructure dev credentials | hypera | Active | 2026-02-19 |
| 10 | hypera-azure-devops-team-az-cli-pim | Azure DevOps Team - AZ CLI PIM credentials | hypera | Active | 2026-02-19 |
| 11 | repos-github-zsh | GitHub credentials for ZSH repository | hypera | Active | 2026-02-19 |
Environment Categories
Azure Environments
hypera-azure-rg-hypera-cafehyna-web-dev- Development Azure resourceshypera-azure-rg-hypera-cafehyna-web- Production Azure resourceshypera-azure-devops-team-az-cli-pim- Azure CLI with PIM integrationAzure OpenAI-finops- Azure OpenAI service configurationdevops-team-azure-quota-automation- Azure quota automation
Kubernetes / Infrastructure
rg-hypera-aks-python-infra-dev- AKS Python infrastructure dev
GitHub Environments
hypera-github-python-devops- Python DevOps automationrepos-github-zsh- ZSH dotfiles repository
Team Environments
devops-team-pim- DevOps team PIM credentialshypera-azure-devops-team-az-cli-pim- Azure DevOps AZ CLI PIM
General
hypera- General infrastructure credentials.dotfiles- Dotfiles configurationdevops-team-azure-quota-automation- Quota automation
CLI Management
All environments were created via CLI using op item create with:
- Category: API Credential
- Tag:
environment - Vault:
hypera - Initial variable:
PLACEHOLDER=initialized(replace with real values)
Populate Variables
Add real variables to replace the placeholder:
# Edit environment to add real variables
op item edit "<env-name>" --vault hypera \
variables.MY_KEY[concealed]=my-value \
variables.PLACEHOLDER[delete]Access via CLI Tools
cd ~/.claude/skills/1password-skill/tools
# OR
cd .claude/skills/1password-skill/tools
# List all environments
bun run list --vault hypera
# Show environment details
bun run show "hypera" hypera
# Export to .env file
bun run export "hypera-azure-rg-hypera-cafehyna-web-dev" hypera > .env
# Create op:// reference
op read "op://hypera/<env-name>/variables/<KEY>"Direct CLI Access
# Read specific variable
op read "op://hypera/hypera/variables/API_KEY"
# Use with op run
op run --env-file .env.tpl -- ./deploy.sh
# List all environment items
op item list --vault hypera --tags environment --format json | jq '.[].title'Secret References Format
All variables use the format:
op://hypera/<env-name>/variables/<KEY>Examples:
op://hypera/hypera/variables/API_KEYop://hypera/devops-team-pim/variables/AZURE_CLIENT_IDop://hypera/Azure OpenAI-finops/variables/OPENAI_API_KEY
Notes
- All environments created 2026-02-19 via CLI (service account access to
hyperavault) - Initial
PLACEHOLDER=initializedvariable should be replaced with real values - Developer Environments is a beta feature in 1Password (GUI-native type different from CLI API Credential items)
- CLI items use
API Credentialcategory +environmenttag as the programmatic equivalent - Use
op-env-updateto add variables once values are known
Developer Environments Reference
Overview
1Password Developer Environments provide a dedicated location to store, organize, and manage project secrets as environment variables. This reference covers both the native GUI feature and CLI tools.
Feature Status
| Feature | GUI Support | CLI Support |
|---|---|---|
| Create environment | Yes | op-env-create.ts |
| Update environment | Yes | op-env-update.ts |
| Delete environment | Yes | op-env-delete.ts |
| Show environment | Yes | op-env-show.ts |
| List environments | Yes | op-env-list.ts |
| Export to .env | Yes | op-env-export.ts |
| Mount .env file | Yes (beta) | No |
| AWS Secrets Manager sync | Yes (beta) | No |
CLI Tools
Tools are written in TypeScript and require Bun runtime.
Setup
# Navigate to tools directory
cd tools
# Run any tool
bun run src/op-env-list.ts --helpAvailable Tools
| Tool | Description |
|---|---|
op-env-create.ts | Create new environment item |
op-env-update.ts | Update existing environment |
op-env-delete.ts | Delete environment item |
op-env-show.ts | Display environment details |
op-env-list.ts | List all environment items |
op-env-export.ts | Export to .env format |
Quick Start
Create Environment
cd tools
# From inline variables
bun run src/op-env-create.ts my-app-dev Personal \
API_KEY=secret123 \
DB_HOST=localhost \
DB_PORT=5432
# From .env file
bun run src/op-env-create.ts my-app-prod Production \
--from-file .env.productionUpdate Environment
# Update single variable
bun run src/op-env-update.ts my-app-dev Personal API_KEY=new-key
# Merge from file
bun run src/op-env-update.ts my-app-dev Personal --from-file .env.local
# Remove variables
bun run src/op-env-update.ts my-app-dev Personal --remove OLD_KEY,DEPRECATEDView & Export
# List all environments
bun run src/op-env-list.ts
# Show details (masked)
bun run src/op-env-show.ts my-app-dev Personal
# Show with values
bun run src/op-env-show.ts my-app-dev Personal --reveal
# Export to .env
bun run src/op-env-export.ts my-app-dev Personal > .env
# Export as template
bun run src/op-env-export.ts my-app-dev Personal --format op-refs > .env.tplDelete Environment
# Interactive
bun run src/op-env-delete.ts old-app Personal
# Force delete
bun run src/op-env-delete.ts old-app Personal --force
# Archive instead
bun run src/op-env-delete.ts old-app Personal --archiveStorage Model
CLI tools store environments as API Credential items with:
- Category: API Credential
- Tags:
environment(configurable) - Section:
variables(contains all env vars) - Field Type: CONCEALED (for all values)
Secret Reference Format
op://<vault>/<environment-name>/variables/<key>Example:
op://Personal/my-app-dev/variables/API_KEYIntegration Patterns
With op run
# Create template
bun run src/op-env-export.ts my-app Production --format op-refs > .env.tpl
# Run command with injected secrets
op run --env-file .env.tpl -- ./deploy.shWith op inject
# Create template
bun run src/op-env-export.ts my-app Production --format op-refs > config.tpl
# Inject secrets into file
op inject -i config.tpl -o config.envIn CI/CD
# GitHub Actions example
- name: Load secrets
run: |
bun run src/op-env-export.ts ci-secrets CI-CD > .env
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}Best Practices
1. Naming Convention: Use project-environment format (e.g., myapp-dev, myapp-prod) 2. Tagging: Always use environment tag for discoverability 3. Vault Organization: Separate vaults per environment or team 4. Access Control: Use 1Password groups for team access 5. Templates: Use --format op-refs for CI/CD pipelines
Limitations
1. GUI-Only Features:
- Local .env file mounting (requires desktop app)
- AWS Secrets Manager sync
2. CLI Workarounds:
- Tools use API Credential category (not native Environment type)
- No direct mapping to GUI Environments view
- Manual sync between GUI environments and CLI items
Related Documentation
- inventory.md - Current environments inventory
- cli-commands.md - Full CLI reference
- 1Password Environments Docs
1Password Kubernetes Integration Examples
Complete Kubernetes manifest examples for integrating 1Password with Kubernetes using either External Secrets Operator or the native 1Password Kubernetes Operator.
External Secrets Operator Integration
Connect Server Deployment
Deploy the 1Password Connect Server that ESO will communicate with.
apiVersion: v1
kind: Namespace
metadata:
name: onepassword-system
---
apiVersion: v1
kind: Secret
metadata:
name: onepassword-credentials
namespace: onepassword-system
type: Opaque
data:
# Base64 encoded 1password-credentials.json
1password-credentials.json: <base64-encoded-credentials>
---
apiVersion: v1
kind: Secret
metadata:
name: onepassword-token
namespace: onepassword-system
type: Opaque
stringData:
token: <your-connect-access-token>
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: onepassword-connect
namespace: onepassword-system
spec:
replicas: 1
selector:
matchLabels:
app: onepassword-connect
template:
metadata:
labels:
app: onepassword-connect
spec:
containers:
- name: connect-api
image: 1password/connect-api:1.7.2
ports:
- containerPort: 8080
name: http
env:
- name: OP_SESSION
valueFrom:
secretKeyRef:
name: onepassword-credentials
key: 1password-credentials.json
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /heartbeat
port: http
initialDelaySeconds: 15
periodSeconds: 30
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 15
- name: connect-sync
image: 1password/connect-sync:1.7.2
env:
- name: OP_SESSION
valueFrom:
secretKeyRef:
name: onepassword-credentials
key: 1password-credentials.json
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
name: onepassword-connect
namespace: onepassword-system
spec:
selector:
app: onepassword-connect
ports:
- port: 8080
targetPort: 8080
name: httpClusterSecretStore Configuration
Define cluster-wide access to 1Password vaults.
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: onepassword
spec:
provider:
onepassword:
connectHost: http://onepassword-connect.onepassword-system:8080
vaults:
production: 1 # Priority ordering
staging: 2
development: 3
auth:
secretRef:
connectTokenSecretRef:
name: onepassword-token
namespace: onepassword-system
key: tokenNamespace-Scoped SecretStore
For namespace-specific vault access.
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata:
name: onepassword-staging
namespace: staging
spec:
provider:
onepassword:
connectHost: http://onepassword-connect.onepassword-system:8080
vaults:
staging: 1
auth:
secretRef:
connectTokenSecretRef:
name: onepassword-token
key: tokenExternalSecret Examples
Basic Secret Retrieval
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: database-credentials
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: onepassword
target:
name: database-credentials
creationPolicy: Owner
deletionPolicy: Retain
data:
- secretKey: DB_HOST
remoteRef:
key: production-database # Item title in 1Password
property: host # Field label
- secretKey: DB_PORT
remoteRef:
key: production-database
property: port
- secretKey: DB_USERNAME
remoteRef:
key: production-database
property: username
- secretKey: DB_PASSWORD
remoteRef:
key: production-database
property: passwordTLS Certificate Secret
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: tls-certificate
namespace: production
spec:
refreshInterval: 24h
secretStoreRef:
kind: ClusterSecretStore
name: onepassword
target:
name: app-tls
creationPolicy: Owner
template:
type: kubernetes.io/tls
data:
- secretKey: tls.crt
remoteRef:
key: wildcard-certificate
property: certificate
- secretKey: tls.key
remoteRef:
key: wildcard-certificate
property: private-keyDocker Registry Secret
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: registry-credentials
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: onepassword
target:
name: registry-secret
creationPolicy: Owner
template:
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: |
{
"auths": {
"{{ .registry }}": {
"username": "{{ .username }}",
"password": "{{ .password }}",
"auth": "{{ printf "%s:%s" .username .password | b64enc }}"
}
}
}
data:
- secretKey: registry
remoteRef:
key: docker-registry
property: registry
- secretKey: username
remoteRef:
key: docker-registry
property: username
- secretKey: password
remoteRef:
key: docker-registry
property: passwordSSH Key Secret
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: git-ssh-key
namespace: argocd
spec:
refreshInterval: 24h
secretStoreRef:
kind: ClusterSecretStore
name: onepassword
target:
name: repo-ssh-key
creationPolicy: Owner
template:
type: kubernetes.io/ssh-auth
data:
- secretKey: ssh-privatekey
remoteRef:
key: git-deploy-key
property: private-keyBulk Environment Variables with dataFrom
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: app-environment
namespace: production
spec:
refreshInterval: 30m
secretStoreRef:
kind: ClusterSecretStore
name: onepassword
target:
name: app-env
creationPolicy: Owner
dataFrom:
- find:
path: app-config # Item title
name:
regexp: "^[A-Z][A-Z0-9_]*$" # Match uppercase env varsMultiple Items with dataFrom
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: combined-secrets
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: onepassword
target:
name: combined-secrets
dataFrom:
- extract:
key: database-credentials
- extract:
key: cache-credentials
- extract:
key: api-keysPushSecret Examples
Push Kubernetes secrets to 1Password.
Basic PushSecret
apiVersion: external-secrets.io/v1alpha1
kind: PushSecret
metadata:
name: push-cert-manager-certs
namespace: cert-manager
spec:
refreshInterval: 1h
secretStoreRefs:
- name: onepassword
kind: ClusterSecretStore
selector:
secret:
name: letsencrypt-certificate
data:
- match:
secretKey: tls.crt
remoteRef:
remoteKey: kubernetes-certificates
property: certificate
metadata:
apiVersion: kubernetes.external-secrets.io/v1alpha1
kind: PushSecretMetadata
spec:
vault: production
tags:
- kubernetes
- certificate
- match:
secretKey: tls.key
remoteRef:
remoteKey: kubernetes-certificates
property: private-key
metadata:
apiVersion: kubernetes.external-secrets.io/v1alpha1
kind: PushSecretMetadata
spec:
vault: production
tags:
- kubernetes
- certificatePushSecret with Delete Policy
apiVersion: external-secrets.io/v1alpha1
kind: PushSecret
metadata:
name: push-dynamic-secret
spec:
deletionPolicy: Delete # Delete from 1Password when PushSecret is deleted
refreshInterval: 30m
secretStoreRefs:
- name: onepassword
kind: ClusterSecretStore
selector:
secret:
name: generated-api-key
data:
- match:
secretKey: api-key
remoteRef:
remoteKey: k8s-generated-api-key
property: password
metadata:
apiVersion: kubernetes.external-secrets.io/v1alpha1
kind: PushSecretMetadata
spec:
vault: stagingNative 1Password Kubernetes Operator
Helm Installation Values
# values.yaml for 1password/connect chart
connect:
create: true
credentials: "" # Set via --set-file
operator:
create: true
watchNamespace: [] # Empty = all namespaces
autoRestart: true
pollingInterval: 600
# Resource limits
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512MiInstall command:
helm install connect 1password/connect \
--namespace onepassword-system \
--create-namespace \
--set-file connect.credentials=1password-credentials.json \
--set operator.token.value=<access-token> \
-f values.yamlOnePasswordItem Examples
Basic Item
apiVersion: onepassword.com/v1
kind: OnePasswordItem
metadata:
name: database-secret
namespace: production
spec:
itemPath: "vaults/Production/items/Database Credentials"Creates a Kubernetes Secret with all fields from the 1Password item.
Item with Specific Vault ID
apiVersion: onepassword.com/v1
kind: OnePasswordItem
metadata:
name: api-keys
namespace: production
spec:
itemPath: "vaults/abcd1234efgh5678/items/API Keys"Auto-Restart Configuration
Namespace-Level Auto-Restart
apiVersion: v1
kind: Namespace
metadata:
name: production
annotations:
operator.1password.io/auto-restart: "true"Deployment with Auto-Restart
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: production
annotations:
operator.1password.io/auto-restart: "true"
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app:latest
envFrom:
- secretRef:
name: database-secret # OnePasswordItem-created secretDisable Auto-Restart for Specific Item
apiVersion: onepassword.com/v1
kind: OnePasswordItem
metadata:
name: static-config
namespace: production
annotations:
operator.1password.io/auto-restart: "false"
spec:
itemPath: "vaults/Production/items/Static Config"Operator Environment Configuration
apiVersion: apps/v1
kind: Deployment
metadata:
name: onepassword-operator
spec:
template:
spec:
containers:
- name: operator
env:
- name: POLLING_INTERVAL
value: "300" # 5 minutes
- name: AUTO_RESTART
value: "true"
- name: WATCH_NAMESPACE
value: "production,staging" # Specific namespaces
- name: OP_CONNECT_HOST
value: "http://onepassword-connect:8080"Kubernetes Secrets Injector
Alternative approach using init containers.
Injector Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
template:
spec:
serviceAccountName: my-app
initContainers:
- name: secrets-injector
image: 1password/kubernetes-secrets-injector:latest
env:
- name: OP_SERVICE_ACCOUNT_TOKEN
valueFrom:
secretKeyRef:
name: op-service-account
key: token
volumeMounts:
- name: secrets
mountPath: /secrets
containers:
- name: app
image: my-app:latest
envFrom:
- secretRef:
name: injected-secrets
volumeMounts:
- name: secrets
mountPath: /secrets
readOnly: true
volumes:
- name: secrets
emptyDir:
medium: MemoryCommon Patterns
Multi-Environment Setup
# Development ClusterSecretStore
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: onepassword-dev
spec:
provider:
onepassword:
connectHost: http://onepassword-connect.onepassword-system:8080
vaults:
development: 1
shared: 2
auth:
secretRef:
connectTokenSecretRef:
name: onepassword-token-dev
namespace: onepassword-system
key: token
---
# Production ClusterSecretStore
apiVersion: external-secrets.io/v1
kind: ClusterSecretStore
metadata:
name: onepassword-prod
spec:
provider:
onepassword:
connectHost: http://onepassword-connect.onepassword-system:8080
vaults:
production: 1
auth:
secretRef:
connectTokenSecretRef:
name: onepassword-token-prod
namespace: onepassword-system
key: tokenKustomize Integration
# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- external-secrets.yaml
patches:
- target:
kind: ExternalSecret
patch: |
- op: replace
path: /spec/secretStoreRef/name
value: onepassword-prodArgoCD Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: app-secrets
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/org/secrets-repo
targetRevision: HEAD
path: external-secrets/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: trueTroubleshooting Manifests
Debug ExternalSecret
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: debug-secret
annotations:
external-secrets.io/debug: "true"
spec:
refreshInterval: 1m # Fast refresh for debugging
secretStoreRef:
kind: ClusterSecretStore
name: onepassword
target:
name: debug-secret
creationPolicy: Owner
data:
- secretKey: test
remoteRef:
key: test-item
property: passwordCheck status:
kubectl describe externalsecret debug-secret
kubectl get externalsecret debug-secret -o yaml
kubectl logs -n onepassword-system -l app=onepassword-connectVerify SecretStore Connection
# Check SecretStore status
kubectl get secretstore,clustersecretstore -A
# Describe for detailed status
kubectl describe clustersecretstore onepassword
# Check for sync errors
kubectl get externalsecret -A -o wide1Password Python SDK Reference
Overview
The onepassword-sdk Python package provides native SDK access to 1Password without shelling out to the op CLI. It offers async operations for secret resolution, item CRUD, and vault management.
System Requirements
- Python: 3.9+
- libssl: 3.x (OpenSSL 3)
- glibc: 2.32+ (Linux)
- macOS: 12+ (Monterey)
Installation
# With uv (recommended)
uv add onepassword-sdk
# With pip
pip install onepassword-sdkAuthentication
Service Account Token (recommended for automation)
import os
from onepassword import Client
client = await Client.authenticate(
auth=os.environ["OP_SERVICE_ACCOUNT_TOKEN"],
integration_name="my-app",
integration_version="1.0.0",
)Environment Variable
Set OP_SERVICE_ACCOUNT_TOKEN in your environment:
export OP_SERVICE_ACCOUNT_TOKEN="ops_..."API Reference
client.secrets
Resolve secret references without fetching full items.
# Single secret
value = await client.secrets.resolve("op://Vault/Item/Field")
# Batch resolve (more efficient for multiple secrets)
values = await client.secrets.resolve_all([
"op://Vault/Item/Field1",
"op://Vault/Item/Field2",
])
# Returns list of strings in same order as inputclient.items
Full CRUD operations on 1Password items.
# List items in a vault
items = await client.items.list_all(vault_id)
async for item in items:
print(item.title, item.id)
# Get a specific item
item = await client.items.get(vault_id, item_id)
# Create an item
from onepassword.types import (
ItemCategory, ItemCreateParams, ItemField, ItemFieldType, ItemSection,
)
params = ItemCreateParams(
title="my-environment",
category=ItemCategory.APICREDENTIALS,
vault_id=vault_id,
tags=["environment"],
sections=[ItemSection(id="variables", title="variables")],
fields=[
ItemField(
id="API_KEY",
title="API_KEY",
value="secret-value",
field_type=ItemFieldType.CONCEALED,
section_id="variables",
),
],
)
created = await client.items.create(params)
# Update an item (get, modify, put)
item = await client.items.get(vault_id, item_id)
# ... modify item fields ...
await client.items.put(vault_id, item)
# Delete an item
await client.items.delete(vault_id, item_id)
# Archive an item
await client.items.archive(vault_id, item_id)client.vaults
# List all accessible vaults
vaults = await client.vaults.list_all()
async for vault in vaults:
print(vault.title, vault.id)Item Creation Patterns
Environment Item (API Credential with variables section)
This is the pattern used by the op-env-* tools:
from onepassword.types import (
ItemCategory, ItemCreateParams, ItemField, ItemFieldType, ItemSection,
)
variables = {"DB_HOST": "localhost", "DB_PORT": "5432", "API_KEY": "secret"}
params = ItemCreateParams(
title="my-app-dev",
category=ItemCategory.APICREDENTIALS,
vault_id=vault_id,
tags=["environment"],
sections=[ItemSection(id="variables", title="variables")],
fields=[
ItemField(
id=key,
title=key,
value=value,
field_type=ItemFieldType.CONCEALED,
section_id="variables",
)
for key, value in variables.items()
],
)
created = await client.items.create(params)Field Types
| SDK Type | CLI Equivalent | Use Case |
|---|---|---|
ItemFieldType.CONCEALED | [concealed] | Secrets, passwords, API keys |
ItemFieldType.TEXT | [text] | Non-sensitive values |
ItemFieldType.URL | [url] | URLs |
ItemFieldType.EMAIL | [email] | Email addresses |
CLI-to-SDK Migration
| CLI Command | SDK Equivalent |
|---|---|
op item list --vault V | client.items.list_all(vault_id) |
op item get ITEM --vault V | client.items.get(vault_id, item_id) |
op item create ... | client.items.create(vault_id, item) |
op item edit ITEM ... | client.items.get() → modify → client.items.put() |
op item delete ITEM | client.items.delete(vault_id, item_id) |
op item delete ITEM --archive | client.items.archive(vault_id, item_id) |
op read "op://V/I/F" | client.secrets.resolve("op://V/I/F") |
op vault list | client.vaults.list_all() |
op item list --tags TAG | No SDK equivalent (use CLI subprocess) |
Key Differences from CLI
1. Vault IDs required — SDK uses vault IDs, not names. Use resolve_vault_id() helper. 2. No tag filtering — items.list_all() doesn't support tag filters. Use CLI subprocess fallback. 3. No get-by-title — Must iterate items to find by title. Use find_item_by_title() helper. 4. Async throughout — All SDK operations are async. Wrap with asyncio.run() for CLI tools. 5. Item update is get+modify+put — No atomic field edit; fetch the full item, modify, then save.
Error Handling
from onepassword import Client
try:
client = await Client.authenticate(auth=token, ...)
except Exception as e:
# Invalid token or network error
print(f"Authentication failed: {e}")
try:
value = await client.secrets.resolve("op://Vault/Item/Field")
except Exception as e:
# Item not found, permission denied, etc.
print(f"Secret resolution failed: {e}")SecretsManager Usage
The SecretsManager class provides a higher-level abstraction with caching:
from op_env.secrets_manager import SecretsManager
async def main():
sm = await SecretsManager.create()
# Single secret (cached after first call)
api_key = await sm.get("op://Production/API/key")
# Batch resolve
secrets = await sm.get_many([
"op://Production/DB/password",
"op://Production/DB/host",
])
# Load all vars from a 1Password environment
env = await sm.resolve_environment("my-app-prod", "Production")
# Returns: {"DB_HOST": "...", "API_KEY": "...", ...}
# List vaults
vaults = await sm.list_vaults()
# Clear cache when needed
sm.clear_cache()FastAPI Integration
from contextlib import asynccontextmanager
from fastapi import FastAPI
from op_env.secrets_manager import SecretsManager
sm: SecretsManager
@asynccontextmanager
async def lifespan(app: FastAPI):
global sm
sm = await SecretsManager.create()
yield
app = FastAPI(lifespan=lifespan)
@app.get("/health")
async def health():
db_url = await sm.get("op://Production/DB/url")
return {"status": "ok"}Django Integration
# settings.py
import asyncio
from op_env.secrets_manager import SecretsManager
def _load_secrets():
async def _resolve():
sm = await SecretsManager.create()
return await sm.resolve_environment("my-app-prod", "Production")
return asyncio.run(_resolve())
_secrets = _load_secrets()
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"HOST": _secrets["DB_HOST"],
"PASSWORD": _secrets["DB_PASSWORD"],
}
}#!/bin/bash
# Setup script for GitHub CLI with 1Password integration
# This script configures gh CLI to use 1Password for authentication
set -e
echo "=== 1Password GitHub CLI Plugin Setup ==="
echo ""
# Check prerequisites
command -v op >/dev/null 2>&1 || { echo "Error: 1Password CLI (op) is not installed"; exit 1; }
command -v gh >/dev/null 2>&1 || { echo "Error: GitHub CLI (gh) is not installed"; exit 1; }
# Sign in to 1Password if needed
echo "Step 1: Checking 1Password authentication..."
if ! op whoami >/dev/null 2>&1; then
echo "Please sign in to 1Password:"
eval $(op signin)
fi
echo "Signed in as: $(op whoami --format json | jq -r '.email')"
echo ""
# List GitHub-related items
echo "Step 2: Available GitHub tokens in 1Password:"
echo "-------------------------------------------"
op item list --categories "API Credential,Login" 2>/dev/null | grep -i github | nl
echo ""
# Prompt for item selection
read -p "Enter the number of the item to use (or 'q' to quit): " selection
if [[ "$selection" == "q" ]]; then
echo "Setup cancelled."
exit 0
fi
# Get the item ID from selection
ITEM_ID=$(op item list --categories "API Credential,Login" 2>/dev/null | grep -i github | sed -n "${selection}p" | awk '{print $1}')
if [[ -z "$ITEM_ID" ]]; then
echo "Error: Invalid selection"
exit 1
fi
ITEM_NAME=$(op item get "$ITEM_ID" --format json | jq -r '.title')
echo ""
echo "Selected: $ITEM_NAME ($ITEM_ID)"
echo ""
# Remove old plugin configuration
echo "Step 3: Clearing old gh plugin configuration..."
rm -f ~/.config/op/plugins/used_items/gh.json 2>/dev/null || true
echo "Done."
echo ""
# Initialize gh plugin
echo "Step 4: Initializing gh plugin with 1Password..."
op plugin init gh
echo ""
# Configure git credential helper
echo "Step 5: Configuring git credential helper..."
# Remove old credential helpers for github.com
git config --global --unset-all credential.https://github.com.helper 2>/dev/null || true
git config --global --unset-all credential.https://gist.github.com.helper 2>/dev/null || true
# Set gh as credential helper
git config --global credential.https://github.com.helper '!/opt/homebrew/bin/gh auth git-credential'
git config --global credential.https://gist.github.com.helper '!/opt/homebrew/bin/gh auth git-credential'
echo "Git credential helper configured."
echo ""
# Verify setup
echo "Step 6: Verifying setup..."
echo ""
echo "GitHub CLI auth status:"
gh auth status
echo ""
echo "=== Setup Complete ==="
echo ""
echo "You can now use git push/pull with GitHub repositories."
echo "The gh CLI will automatically use 1Password for authentication."
echo ""
echo "To test, run:"
echo " git push origin main"
#!/usr/bin/env bash
#
# setup-service-account.sh
# Create and configure a 1Password service account for automation
#
# Usage:
# ./setup-service-account.sh <name> <vault> [permissions]
#
# Arguments:
# name - Name for the service account (e.g., "CI-CD-Pipeline")
# vault - Vault name or ID to grant access to
# permissions - Comma-separated permissions (default: read_items)
# Options: read_items, write_items, share_items
#
# Examples:
# ./setup-service-account.sh "GitHub-Actions" "Production" "read_items"
# ./setup-service-account.sh "Deploy-Bot" "Staging" "read_items,write_items"
#
# Requirements:
# - 1Password CLI (op) version 2.18.0 or later
# - Signed in to 1Password account with admin permissions
#
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Print functions
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
# Check prerequisites
check_prerequisites() {
info "Checking prerequisites..."
# Check if op CLI is installed
if ! command -v op &> /dev/null; then
error "1Password CLI (op) is not installed."
echo "Install it from: https://1password.com/downloads/command-line"
exit 1
fi
# Check op version
OP_VERSION=$(op --version | head -1)
info "Found 1Password CLI version: $OP_VERSION"
# Check if signed in
if ! op whoami &> /dev/null; then
error "Not signed in to 1Password."
echo "Run 'op signin' first."
exit 1
fi
ACCOUNT=$(op whoami --format json | jq -r '.email // .url')
success "Signed in as: $ACCOUNT"
}
# Validate vault exists
validate_vault() {
local vault="$1"
info "Validating vault: $vault"
if ! op vault get "$vault" &> /dev/null; then
error "Vault '$vault' not found or not accessible."
echo ""
echo "Available vaults:"
op vault list --format json | jq -r '.[] | " - \(.name) (\(.id))"'
exit 1
fi
VAULT_ID=$(op vault get "$vault" --format json | jq -r '.id')
success "Found vault: $vault (ID: $VAULT_ID)"
}
# Parse and validate permissions
parse_permissions() {
local perms="$1"
local valid_perms=("read_items" "write_items" "share_items")
IFS=',' read -ra PERM_ARRAY <<< "$perms"
for perm in "${PERM_ARRAY[@]}"; do
perm=$(echo "$perm" | tr -d ' ')
local found=false
for valid in "${valid_perms[@]}"; do
if [[ "$perm" == "$valid" ]]; then
found=true
break
fi
done
if [[ "$found" == "false" ]]; then
error "Invalid permission: $perm"
echo "Valid permissions: ${valid_perms[*]}"
exit 1
fi
done
success "Permissions validated: $perms"
}
# Create service account
create_service_account() {
local name="$1"
local vault="$2"
local permissions="$3"
info "Creating service account: $name"
info "Vault: $vault"
info "Permissions: $permissions"
echo ""
# Build the command
local cmd="op service-account create \"$name\" --vault \"$vault:$permissions\""
info "Running: $cmd"
echo ""
# Execute and capture the token
local output
if output=$(op service-account create "$name" --vault "$vault:$permissions" 2>&1); then
echo ""
success "Service account created successfully!"
echo ""
echo -e "${YELLOW}========================================${NC}"
echo -e "${YELLOW} IMPORTANT: Save this token securely! ${NC}"
echo -e "${YELLOW} It will NOT be shown again. ${NC}"
echo -e "${YELLOW}========================================${NC}"
echo ""
echo "$output"
echo ""
echo -e "${BLUE}Usage:${NC}"
echo " export OP_SERVICE_ACCOUNT_TOKEN=\"<token>\""
echo " op vault list # Test connection"
echo ""
echo -e "${BLUE}In CI/CD:${NC}"
echo " Store the token as a secret named OP_SERVICE_ACCOUNT_TOKEN"
echo ""
else
error "Failed to create service account:"
echo "$output"
exit 1
fi
}
# Show usage
usage() {
echo "Usage: $0 <name> <vault> [permissions]"
echo ""
echo "Arguments:"
echo " name - Name for the service account"
echo " vault - Vault name or ID to grant access to"
echo " permissions - Comma-separated permissions (default: read_items)"
echo " Options: read_items, write_items, share_items"
echo ""
echo "Examples:"
echo " $0 \"GitHub-Actions\" \"Production\" \"read_items\""
echo " $0 \"Deploy-Bot\" \"Staging\" \"read_items,write_items\""
exit 1
}
# Main
main() {
# Check arguments
if [[ $# -lt 2 ]]; then
usage
fi
local name="$1"
local vault="$2"
local permissions="${3:-read_items}"
echo ""
echo "1Password Service Account Setup"
echo "================================"
echo ""
check_prerequisites
echo ""
validate_vault "$vault"
echo ""
parse_permissions "$permissions"
echo ""
# Confirm before creating
echo -e "${YELLOW}About to create service account:${NC}"
echo " Name: $name"
echo " Vault: $vault"
echo " Permissions: $permissions"
echo ""
read -p "Continue? (y/N) " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
info "Aborted."
exit 0
fi
echo ""
create_service_account "$name" "$vault" "$permissions"
}
main "$@"
#!/usr/bin/env bash
#
# sync-check.sh
# Verify External Secrets Operator synchronization with 1Password
#
# Usage:
# ./sync-check.sh [namespace]
#
# Arguments:
# namespace - Kubernetes namespace to check (default: all namespaces)
#
# Requirements:
# - kubectl configured with cluster access
# - External Secrets Operator installed
# - 1Password Connect Server running
#
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Print functions
info() { echo -e "${BLUE}[INFO]${NC} $1"; }
success() { echo -e "${GREEN}[OK]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }
header() { echo -e "\n${CYAN}=== $1 ===${NC}\n"; }
# Check if kubectl is available
check_kubectl() {
if ! command -v kubectl &> /dev/null; then
error "kubectl is not installed or not in PATH"
exit 1
fi
if ! kubectl cluster-info &> /dev/null; then
error "Cannot connect to Kubernetes cluster"
exit 1
fi
local context
context=$(kubectl config current-context)
info "Connected to cluster: $context"
}
# Check SecretStore status
check_secret_stores() {
header "SecretStore Status"
local stores
stores=$(kubectl get secretstore,clustersecretstore -A -o json 2>/dev/null || echo '{"items":[]}')
local count
count=$(echo "$stores" | jq '.items | length')
if [[ "$count" -eq 0 ]]; then
warn "No SecretStores found"
return
fi
echo "$stores" | jq -r '.items[] | [
.kind,
.metadata.namespace // "cluster-scoped",
.metadata.name,
(.status.conditions[]? | select(.type=="Ready") | .status) // "Unknown"
] | @tsv' | while IFS=$'\t' read -r kind namespace name ready; do
if [[ "$ready" == "True" ]]; then
success "$kind/$name (ns: $namespace) - Ready"
else
error "$kind/$name (ns: $namespace) - Not Ready"
fi
done
}
# Check ExternalSecret status
check_external_secrets() {
local namespace="$1"
header "ExternalSecret Status"
local ns_flag=""
if [[ -n "$namespace" ]]; then
ns_flag="-n $namespace"
else
ns_flag="-A"
fi
local secrets
secrets=$(kubectl get externalsecret $ns_flag -o json 2>/dev/null || echo '{"items":[]}')
local count
count=$(echo "$secrets" | jq '.items | length')
if [[ "$count" -eq 0 ]]; then
warn "No ExternalSecrets found"
return
fi
info "Found $count ExternalSecret(s)"
echo ""
local synced=0
local failed=0
local pending=0
echo "$secrets" | jq -r '.items[] | [
.metadata.namespace,
.metadata.name,
(.status.conditions[]? | select(.type=="Ready") | .status) // "Unknown",
(.status.conditions[]? | select(.type=="Ready") | .message) // "N/A",
.status.refreshTime // "Never"
] | @tsv' | while IFS=$'\t' read -r ns name ready message refresh; do
local status_icon
case "$ready" in
"True")
status_icon="${GREEN}✓${NC}"
((synced++)) || true
;;
"False")
status_icon="${RED}✗${NC}"
((failed++)) || true
;;
*)
status_icon="${YELLOW}?${NC}"
((pending++)) || true
;;
esac
echo -e "$status_icon $ns/$name"
echo " Status: $ready"
echo " Message: $message"
echo " Last Refresh: $refresh"
echo ""
done
echo "---"
echo "Summary:"
kubectl get externalsecret $ns_flag -o json | jq -r '
.items |
group_by(.status.conditions[]? | select(.type=="Ready") | .status) |
map({
status: (.[0].status.conditions[]? | select(.type=="Ready") | .status) // "Unknown",
count: length
}) |
.[] |
" \(.status): \(.count)"
'
}
# Check 1Password Connect Server
check_connect_server() {
header "1Password Connect Server"
# Try to find Connect Server deployment
local connect_deploy
connect_deploy=$(kubectl get deploy -A -l app=onepassword-connect -o json 2>/dev/null || echo '{"items":[]}')
local count
count=$(echo "$connect_deploy" | jq '.items | length')
if [[ "$count" -eq 0 ]]; then
# Try alternative label
connect_deploy=$(kubectl get deploy -A -o json 2>/dev/null | jq '[.items[] | select(.metadata.name | contains("onepassword") or contains("1password"))]' || echo '[]')
count=$(echo "$connect_deploy" | jq 'length')
fi
if [[ "$count" -eq 0 ]]; then
warn "1Password Connect Server not found"
info "Looking for any 1Password related pods..."
kubectl get pods -A | grep -i "password\|1password\|onepassword" || warn "No 1Password pods found"
return
fi
echo "$connect_deploy" | jq -r '.[] // .items[] | [
.metadata.namespace,
.metadata.name,
"\(.status.readyReplicas // 0)/\(.spec.replicas)"
] | @tsv' | while IFS=$'\t' read -r ns name replicas; do
local ready
ready=$(echo "$replicas" | cut -d'/' -f1)
local desired
desired=$(echo "$replicas" | cut -d'/' -f2)
if [[ "$ready" -eq "$desired" && "$ready" -gt 0 ]]; then
success "Deployment $ns/$name ($replicas replicas)"
else
error "Deployment $ns/$name ($replicas replicas) - Not healthy"
fi
done
# Check pods
info "Connect Server Pods:"
kubectl get pods -A -l app=onepassword-connect 2>/dev/null || \
kubectl get pods -A | grep -i "onepassword-connect\|1password-connect" || \
warn "No Connect Server pods found"
}
# Check for sync errors
check_sync_errors() {
local namespace="$1"
header "Sync Errors"
local ns_flag=""
if [[ -n "$namespace" ]]; then
ns_flag="-n $namespace"
else
ns_flag="-A"
fi
local errors
errors=$(kubectl get externalsecret $ns_flag -o json 2>/dev/null | jq -r '
.items[] |
select(.status.conditions[]? | select(.type=="Ready" and .status=="False")) |
{
namespace: .metadata.namespace,
name: .metadata.name,
message: (.status.conditions[] | select(.type=="Ready") | .message)
}
')
if [[ -z "$errors" || "$errors" == "null" ]]; then
success "No sync errors found"
return
fi
error "Found sync errors:"
echo "$errors" | jq -r '" \(.namespace)/\(.name): \(.message)"'
}
# Check recent events
check_events() {
local namespace="$1"
header "Recent Events"
local ns_flag=""
if [[ -n "$namespace" ]]; then
ns_flag="-n $namespace"
else
ns_flag="-A"
fi
info "External Secrets related events (last 10):"
kubectl get events $ns_flag --sort-by='.lastTimestamp' -o json 2>/dev/null | jq -r '
[.items[] | select(.involvedObject.kind == "ExternalSecret" or .involvedObject.kind == "SecretStore" or .involvedObject.kind == "ClusterSecretStore")] |
reverse |
.[:10] |
.[] |
"\(.lastTimestamp) [\(.type)] \(.involvedObject.kind)/\(.involvedObject.name): \(.message)"
' || warn "Could not fetch events"
}
# Health check summary
print_summary() {
header "Health Check Summary"
local checks_passed=0
local checks_failed=0
# Check SecretStores
local store_status
store_status=$(kubectl get secretstore,clustersecretstore -A -o json 2>/dev/null | jq -r '
[.items[].status.conditions[]? | select(.type=="Ready" and .status=="True")] | length
')
local store_total
store_total=$(kubectl get secretstore,clustersecretstore -A -o json 2>/dev/null | jq '.items | length')
if [[ "$store_status" -eq "$store_total" && "$store_total" -gt 0 ]]; then
success "SecretStores: $store_status/$store_total ready"
((checks_passed++))
else
error "SecretStores: $store_status/$store_total ready"
((checks_failed++))
fi
# Check ExternalSecrets
local es_ready
es_ready=$(kubectl get externalsecret -A -o json 2>/dev/null | jq -r '
[.items[].status.conditions[]? | select(.type=="Ready" and .status=="True")] | length
')
local es_total
es_total=$(kubectl get externalsecret -A -o json 2>/dev/null | jq '.items | length')
if [[ "$es_ready" -eq "$es_total" && "$es_total" -gt 0 ]]; then
success "ExternalSecrets: $es_ready/$es_total synced"
((checks_passed++))
elif [[ "$es_total" -eq 0 ]]; then
warn "ExternalSecrets: None found"
else
error "ExternalSecrets: $es_ready/$es_total synced"
((checks_failed++))
fi
echo ""
if [[ "$checks_failed" -eq 0 ]]; then
success "All checks passed!"
else
error "$checks_failed check(s) failed"
exit 1
fi
}
# Show usage
usage() {
echo "Usage: $0 [namespace]"
echo ""
echo "Verify External Secrets Operator synchronization with 1Password"
echo ""
echo "Arguments:"
echo " namespace - Kubernetes namespace to check (default: all namespaces)"
echo ""
echo "Examples:"
echo " $0 # Check all namespaces"
echo " $0 production # Check only production namespace"
exit 0
}
# Main
main() {
local namespace=""
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
usage
;;
*)
namespace="$1"
shift
;;
esac
done
echo ""
echo "1Password External Secrets Sync Check"
echo "======================================"
check_kubectl
if [[ -n "$namespace" ]]; then
info "Checking namespace: $namespace"
else
info "Checking all namespaces"
fi
check_secret_stores
check_connect_server
check_external_secrets "$namespace"
check_sync_errors "$namespace"
check_events "$namespace"
print_summary
}
main "$@"
# Docker Compose with 1Password environment injection
# Run with: op run --env-file .env.tpl -- docker compose up
#
# Create .env.tpl first:
# op-env-export.sh my-app-dev Development --format op-refs > .env.tpl
services:
app:
build: .
ports:
- "3000:3000"
environment:
# These will be injected from 1Password via op run
- NODE_ENV=${APP_ENV:-development}
- API_KEY=${API_KEY}
- DB_HOST=${DB_HOST:-db}
- DB_PORT=${DB_PORT:-5432}
- DB_NAME=${DB_NAME}
- DB_USER=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- REDIS_URL=redis://${REDIS_HOST:-redis}:${REDIS_PORT:-6379}
depends_on:
- db
- redis
db:
image: postgres:16
environment:
- POSTGRES_DB=${DB_NAME}
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7
command: redis-server --requirepass ${REDIS_PASSWORD:-}
ports:
- "6379:6379"
volumes:
postgres_data:
# Usage:
# 1. Create environment in 1Password:
# op-env-create.sh my-app-dev Development \
# DB_NAME=myapp DB_USER=admin DB_PASSWORD=secret ...
#
# 2. Export as template:
# op-env-export.sh my-app-dev Development --format op-refs > .env.tpl
#
# 3. Run with secrets injection:
# op run --env-file .env.tpl -- docker compose up
# 1Password Secret Reference Template
# Use with: op run --env-file .env.tpl -- <command>
# Or: op inject -i .env.tpl -o .env
#
# Replace <vault> and <env-name> with your actual values
# Application
APP_NAME=my-app
APP_ENV=production
DEBUG=false
# API Keys (from 1Password)
API_KEY=op://<vault>/<env-name>/variables/API_KEY
API_SECRET=op://<vault>/<env-name>/variables/API_SECRET
# Database (from 1Password)
DB_HOST=op://<vault>/<env-name>/variables/DB_HOST
DB_PORT=op://<vault>/<env-name>/variables/DB_PORT
DB_NAME=op://<vault>/<env-name>/variables/DB_NAME
DB_USER=op://<vault>/<env-name>/variables/DB_USER
DB_PASSWORD=op://<vault>/<env-name>/variables/DB_PASSWORD
# Redis (from 1Password)
REDIS_HOST=op://<vault>/<env-name>/variables/REDIS_HOST
REDIS_PORT=op://<vault>/<env-name>/variables/REDIS_PORT
REDIS_PASSWORD=op://<vault>/<env-name>/variables/REDIS_PASSWORD
# AWS (from 1Password)
AWS_ACCESS_KEY_ID=op://<vault>/<env-name>/variables/AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY=op://<vault>/<env-name>/variables/AWS_SECRET_ACCESS_KEY
AWS_REGION=op://<vault>/<env-name>/variables/AWS_REGION
# Azure (from 1Password)
AZURE_SUBSCRIPTION_ID=op://<vault>/<env-name>/variables/AZURE_SUBSCRIPTION_ID
AZURE_TENANT_ID=op://<vault>/<env-name>/variables/AZURE_TENANT_ID
AZURE_CLIENT_ID=op://<vault>/<env-name>/variables/AZURE_CLIENT_ID
AZURE_CLIENT_SECRET=op://<vault>/<env-name>/variables/AZURE_CLIENT_SECRET
# 1Password Environment Template
# Copy this file to .env.local and fill in your values
# Then create environment: op-env-create.sh <name> <vault> --from-file .env.local
# Application
APP_NAME=my-app
APP_ENV=development
DEBUG=true
# API Keys
API_KEY=
API_SECRET=
# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=myapp_dev
DB_USER=
DB_PASSWORD=
# Redis/Cache
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
# AWS (if applicable)
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=us-east-1
# Azure (if applicable)
AZURE_SUBSCRIPTION_ID=
AZURE_TENANT_ID=
AZURE_CLIENT_ID=
AZURE_CLIENT_SECRET=
# External Services
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
SMTP_PASSWORD=
# Monitoring
SENTRY_DSN=
DATADOG_API_KEY=
# GitHub Actions workflow using 1Password environments
# Loads secrets from 1Password environment and injects them into the job
#
# Prerequisites:
# 1. Create service account: op service-account create "GitHub Actions" --vault <vault>:read_items
# 2. Add OP_SERVICE_ACCOUNT_TOKEN to GitHub repository secrets
# 3. Create environment in 1Password: op-env-create.sh ci-cd-secrets <vault> ...
name: Deploy with 1Password Environments
on:
push:
branches: [main]
workflow_dispatch:
env:
# Reference your 1Password vault and environment
OP_VAULT: CI-CD
OP_ENVIRONMENT: ci-cd-secrets
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install 1Password CLI
uses: 1password/install-cli-action@v1
- name: Load secrets from 1Password environment
id: load-secrets
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
run: |
# Export all variables from the environment
# Using the op-env-export.sh approach
# Read each secret individually
echo "API_KEY=$(op read 'op://${{ env.OP_VAULT }}/${{ env.OP_ENVIRONMENT }}/variables/API_KEY')" >> $GITHUB_ENV
echo "DB_PASSWORD=$(op read 'op://${{ env.OP_VAULT }}/${{ env.OP_ENVIRONMENT }}/variables/DB_PASSWORD')" >> $GITHUB_ENV
# Or use op run with a template file
# op run --env-file .env.tpl -- env >> $GITHUB_ENV
- name: Deploy application
run: |
# Secrets are now available as environment variables
./deploy.sh
env:
API_KEY: ${{ env.API_KEY }}
DB_PASSWORD: ${{ env.DB_PASSWORD }}
# Alternative approach using 1password/load-secrets-action
deploy-with-action:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Load secrets
uses: 1password/load-secrets-action@v2
with:
export-env: true
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
# Map secrets from 1Password to environment variables
API_KEY: op://CI-CD/ci-cd-secrets/variables/API_KEY
DB_HOST: op://CI-CD/ci-cd-secrets/variables/DB_HOST
DB_PASSWORD: op://CI-CD/ci-cd-secrets/variables/DB_PASSWORD
- name: Deploy
run: ./deploy.sh
[project]
name = "op-env-tools"
version = "0.1.0"
description = "1Password Developer Environment management tools using the Python SDK"
requires-python = ">=3.9"
dependencies = [
"onepassword-sdk>=0.1.7",
]
[project.scripts]
op-env-create = "op_env.op_env_create:main_sync"
op-env-list = "op_env.op_env_list:main_sync"
op-env-show = "op_env.op_env_show:main_sync"
op-env-update = "op_env.op_env_update:main_sync"
op-env-export = "op_env.op_env_export:main_sync"
op-env-delete = "op_env.op_env_delete:main_sync"
[dependency-groups]
dev = [
"pytest>=8.0",
"pytest-asyncio>=0.23",
"ruff>=0.4",
"mypy>=1.10",
]
[tool.ruff]
target-version = "py39"
line-length = 100
[tool.ruff.lint]
select = ["E", "F", "I", "N", "W", "UP"]
[tool.mypy]
python_version = "3.9"
strict = true
warn_return_any = true
warn_unused_configs = true
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/op_env"]
1Password Python SDK Tools
Python CLI tools for managing 1Password Developer Environments using the official onepassword-sdk package. Drop-in replacement for the TypeScript/Bun tools with identical CLI interfaces.
Prerequisites
- Python 3.9+
- uv package manager
OP_SERVICE_ACCOUNT_TOKENenvironment variable set
Setup
cd tools-python
uv syncCLI Tools
All tools accept the same arguments as their TypeScript counterparts.
Create Environment
# Inline variables
uv run op-env-create my-app-dev Personal API_KEY=secret DB_HOST=localhost
# From .env file
uv run op-env-create my-app-prod Production --from-file .env.prod
# Combine file + inline (inline overrides)
uv run op-env-create azure-config Shared --from-file .env EXTRA=valueList Environments
uv run op-env-list
uv run op-env-list --vault Personal
uv run op-env-list --jsonShow Environment
uv run op-env-show my-app-dev Personal
uv run op-env-show my-app-dev Personal --reveal
uv run op-env-show my-app-dev Personal --json
uv run op-env-show my-app-dev Personal --keysUpdate Environment
uv run op-env-update my-app-dev Personal API_KEY=new-key
uv run op-env-update my-app-dev Personal --from-file .env.local
uv run op-env-update my-app-dev Personal --remove OLD_KEY,DEPRECATEDExport Environment
uv run op-env-export my-app-dev Personal > .env
uv run op-env-export my-app-dev Personal --format docker > .env
uv run op-env-export my-app-dev Personal --format op-refs > .env.tpl
uv run op-env-export my-app-dev Personal --format json
uv run op-env-export azure Shared --prefix AZURE_ > .envDelete Environment
uv run op-env-delete my-app-dev Personal
uv run op-env-delete old-config Shared --force
uv run op-env-delete deprecated Production --archiveSecretsManager (for application integration)
from op_env.secrets_manager import SecretsManager
async def main():
sm = await SecretsManager.create()
# Single secret (with caching)
api_key = await sm.get("op://Production/API/key")
# Batch resolve
secrets = await sm.get_many([
"op://Production/DB/password",
"op://Production/DB/host",
])
# Load all vars from an environment item
env = await sm.resolve_environment("my-app-prod", "Production")SDK vs CLI
| Feature | Python SDK (tools-python/) | TypeScript CLI (tools/) |
|---|---|---|
| Runtime | Python 3.9+ / uv | Bun |
| Auth | Service account token | op CLI signin or token |
| Performance | Native SDK (no subprocess) | Shells out to op CLI |
| Tag filtering | Falls back to CLI | Native CLI support |
| Best for | Python apps, FastAPI/Django | Scripts, CI/CD, shell |
Development
# Lint
uv run ruff check src/
# Type check
uv run mypy src/
# Run tests
uv run pytest"""1Password Developer Environment management tools using the Python SDK."""
"""op-env-create — Create environment item in 1Password (Python SDK version).
Usage:
op-env-create <name> <vault> [--from-file <.env>] [--tags <tags>] [KEY=VALUE ...]
"""
from __future__ import annotations
import argparse
import asyncio
import os
import sys
from onepassword.types import (
ItemCategory,
ItemCreateParams,
ItemField,
ItemFieldType,
ItemSection,
)
from .utils import (
get_client,
item_exists,
log,
parse_env_file,
parse_inline_vars,
resolve_vault_id,
)
HELP_EPILOG = """\
examples:
# Create with inline variables
uv run op-env-create my-app-dev Development API_KEY=xxx DB_HOST=localhost
# Create from .env file
uv run op-env-create my-app-prod Production --from-file .env.prod
# Combine file and inline (inline overrides file)
uv run op-env-create azure-config Shared --from-file .env EXTRA_KEY=value
# With custom tags
uv run op-env-create secrets DevOps --tags "env,production,api" KEY=value
"""
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
prog="op-env-create",
description="Create environment item in 1Password",
epilog=HELP_EPILOG,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("positionals", nargs="*", help="<name> <vault> [KEY=VALUE ...]")
parser.add_argument("--from-file", dest="from_file", help="Import variables from .env file")
parser.add_argument(
"--tags", default="environment", help="Comma-separated tags (default: environment)"
)
return parser
async def main() -> None:
parser = build_parser()
args = parser.parse_args()
# Separate name, vault, and KEY=VALUE from positionals
name = ""
vault = ""
env_args: list[str] = []
for arg in args.positionals:
if "=" in arg:
env_args.append(arg)
elif not name:
name = arg
elif not vault:
vault = arg
if not name or not vault:
log.error("Missing required arguments: name and vault")
parser.print_help()
sys.exit(1)
client = await get_client()
vault_id = await resolve_vault_id(client, vault)
# Check if item already exists
if await item_exists(client, vault_id, name):
log.error(f"Item '{name}' already exists in vault '{vault}'")
log.info("Use op-env-update to modify existing environment")
sys.exit(1)
# Collect variables
variables: dict[str, str] = {}
# Load from file if specified
if args.from_file:
if not os.path.isfile(args.from_file):
log.error(f"File not found: {args.from_file}")
sys.exit(1)
log.info(f"Loading variables from: {args.from_file}")
with open(args.from_file) as f:
variables.update(parse_env_file(f.read()))
# Add/override with inline variables
variables.update(parse_inline_vars(env_args))
if not variables:
log.error("No environment variables provided")
log.info("Use --from-file <.env> or provide KEY=value pairs")
sys.exit(1)
log.info(f"Creating environment: {name} in vault: {vault}")
log.info(f"Variables ({len(variables)}): {' '.join(variables.keys())}")
# Build the item using the SDK
# Create fields in a "variables" section with CONCEALED type
section = ItemSection(id="variables", title="variables")
fields = []
for key, value in variables.items():
fields.append(
ItemField(
id=key,
title=key,
value=value,
field_type=ItemFieldType.CONCEALED,
section_id="variables",
)
)
params = ItemCreateParams(
title=name,
category=ItemCategory.APICREDENTIALS,
vault_id=vault_id,
tags=args.tags.split(","),
sections=[section],
fields=fields,
)
try:
await client.items.create(params)
log.info(f"Environment '{name}' created successfully")
print("\nVariables stored:")
for key in variables:
print(f" - {key}")
print("\nUsage:")
print(" # Export to .env file")
print(f" uv run op-env-export '{name}' '{vault}' > .env")
print()
first_key = next(iter(variables))
print(" # Read single variable")
print(f" op read 'op://{vault}/{name}/variables/{first_key}'")
except Exception as exc:
log.error(f"Failed to create environment: {exc}")
sys.exit(1)
def main_sync() -> None:
asyncio.run(main())
if __name__ == "__main__":
main_sync()
"""op-env-delete — Delete environment item from 1Password (Python SDK version).
Usage:
op-env-delete <name> <vault> [--force] [--archive]
"""
from __future__ import annotations
import argparse
import asyncio
import sys
from .utils import (
find_item_by_title,
get_client,
log,
resolve_vault_id,
)
HELP_EPILOG = """\
examples:
# Interactive deletion
uv run op-env-delete my-app-dev Development
# Force delete without confirmation
uv run op-env-delete old-config Shared --force
# Archive instead of delete
uv run op-env-delete deprecated-env Production --archive
"""
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
prog="op-env-delete",
description="Delete environment item from 1Password",
epilog=HELP_EPILOG,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("name", help="Name/title of the environment item")
parser.add_argument("vault", help="Vault containing the item")
parser.add_argument(
"--force", "-f", action="store_true", help="Skip confirmation prompt"
)
parser.add_argument(
"--archive", action="store_true", help="Archive instead of permanent delete"
)
return parser
async def main() -> None:
parser = build_parser()
args = parser.parse_args()
client = await get_client()
vault_id = await resolve_vault_id(client, args.vault)
# Find the item
item = await find_item_by_title(client, vault_id, args.name)
if item is None:
log.error(f"Item '{args.name}' not found in vault '{args.vault}'")
sys.exit(1)
item_id = item.id
log.info("Environment to delete:")
print(f" Name: {args.name}")
print(f" Vault: {args.vault}")
print()
# Confirm unless force
if not args.force:
action = "Archive" if args.archive else "Permanently DELETE"
try:
response = input(f"{action} this environment? [y/N] ").strip().lower()
except (EOFError, KeyboardInterrupt):
print()
log.info("Cancelled")
sys.exit(0)
if response not in ("y", "yes"):
log.info("Cancelled")
sys.exit(0)
# Delete or archive
try:
if args.archive:
await client.items.archive(vault_id, item_id)
log.info(f"Environment '{args.name}' archived successfully")
else:
await client.items.delete(vault_id, item_id)
log.info(f"Environment '{args.name}' deleted permanently")
except Exception as exc:
log.error(f"Failed to delete environment: {exc}")
sys.exit(1)
def main_sync() -> None:
asyncio.run(main())
if __name__ == "__main__":
main_sync()
"""op-env-export — Export environment to .env file format (Python SDK version).
Usage:
op-env-export <name> <vault> [--format <fmt>] [--prefix <str>] > .env
"""
from __future__ import annotations
import argparse
import asyncio
import json
import sys
from datetime import datetime, timezone
from .utils import (
extract_variables,
find_item_by_title,
get_client,
log,
resolve_vault_id,
)
HELP_EPILOG = """\
examples:
# Export to .env file
uv run op-env-export my-app-dev Development > .env
# Docker-compatible format
uv run op-env-export my-app Development --format docker > .env
# Create template with op:// references
uv run op-env-export my-app-prod Production --format op-refs > .env.tpl
# JSON format
uv run op-env-export config Shared --format json
# Add prefix to all variables
uv run op-env-export azure Shared --prefix AZURE_ > .env
"""
VALID_FORMATS = ("env", "docker", "op-refs", "json")
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
prog="op-env-export",
description="Export environment to .env file format",
epilog=HELP_EPILOG,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("name", help="Name/title of the environment item")
parser.add_argument("vault", help="Vault containing the item")
parser.add_argument(
"--format",
dest="fmt",
default="env",
choices=VALID_FORMATS,
help="Output format (default: env)",
)
parser.add_argument("--prefix", default="", help="Add prefix to all variable names")
return parser
async def main() -> None:
parser = build_parser()
args = parser.parse_args()
client = await get_client()
vault_id = await resolve_vault_id(client, args.vault)
# Find the item
item = await find_item_by_title(client, vault_id, args.name)
if item is None:
log.error(f"Item '{args.name}' not found in vault '{args.vault}'")
sys.exit(1)
log.info(f"Exporting: {args.name} from {args.vault} (format: {args.fmt})")
# Extract variable names
variables = extract_variables(item)
if not variables:
log.error("No variables found in environment")
sys.exit(1)
# Resolve actual values using batch resolution
refs = [f"op://{args.vault}/{args.name}/variables/{key}" for key in variables]
try:
resolved_values = await client.secrets.resolve_all(refs)
except Exception:
# Fallback: resolve individually
resolved_values = []
for ref in refs:
try:
resolved_values.append(await client.secrets.resolve(ref))
except Exception:
resolved_values.append(variables[list(variables.keys())[len(resolved_values)]])
revealed: dict[str, str] = dict(zip(variables.keys(), resolved_values))
# Output header
timestamp = datetime.now(timezone.utc).isoformat()
prefix = args.prefix
if args.fmt in ("env", "docker"):
print(f"# Generated from 1Password: {args.name} ({args.vault})")
print(f"# Date: {timestamp}")
print()
elif args.fmt == "op-refs":
print(f"# 1Password Template: {args.name} ({args.vault})")
print("# Use with: op inject -i .env.tpl -o .env")
print("# Or: op run --env-file .env.tpl -- command")
print()
# Output variables
if args.fmt == "env":
for key, value in revealed.items():
escaped = value.replace("\\", "\\\\").replace('"', '\\"')
print(f"{prefix}{key}={escaped}")
elif args.fmt == "docker":
for key, value in revealed.items():
escaped = value.replace("\\", "\\\\").replace('"', '\\"')
print(f'{prefix}{key}="{escaped}"')
elif args.fmt == "op-refs":
for key in revealed:
print(f"{prefix}{key}=op://{args.vault}/{args.name}/variables/{key}")
elif args.fmt == "json":
obj = {f"{prefix}{key}": value for key, value in revealed.items()}
print(json.dumps(obj, indent=2))
log.info("Export complete")
def main_sync() -> None:
asyncio.run(main())
if __name__ == "__main__":
main_sync()
"""op-env-list — List environment items from 1Password (Python SDK version).
Usage:
op-env-list [--vault <vault>] [--tags <tags>] [--json]
"""
from __future__ import annotations
import argparse
import asyncio
import json
from .utils import colors, get_client, list_environments
HELP_EPILOG = """\
examples:
# List all environments
uv run op-env-list
# Filter by vault
uv run op-env-list --vault Development
# Filter by tags
uv run op-env-list --tags environment,production
# JSON output
uv run op-env-list --json
"""
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
prog="op-env-list",
description="List environment items from 1Password",
epilog=HELP_EPILOG,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("--vault", help="Filter by vault name")
parser.add_argument(
"--tags", default="environment", help="Filter by tags (default: environment)"
)
parser.add_argument("--json", dest="json_output", action="store_true", help="JSON output")
return parser
async def main() -> None:
parser = build_parser()
args = parser.parse_args()
# list_environments uses the op CLI (hybrid approach — SDK lacks tag filtering)
# We still call get_client() to validate the token is set, but the listing
# itself goes through the CLI.
_ = await get_client()
items = await list_environments(vault=args.vault, tags=args.tags)
# JSON output mode
if args.json_output:
print(json.dumps(items, indent=2))
return
if not items:
print("No environments found")
print()
print("Tip: Create one with: uv run op-env-create <name> <vault> KEY=value")
return
c = colors
print()
print(c.cyan("+" + "=" * 79 + "+"))
print(f"{c.cyan('|')}{'1Password Environments':^79}{c.cyan('|')}")
print(c.cyan("+" + "=" * 79 + "+"))
# Header
name_h = "NAME".ljust(35)
vault_h = "VAULT".ljust(20)
updated_h = "UPDATED".ljust(15)
print(f"{c.cyan('|')} {name_h} | {vault_h} | {updated_h} {c.cyan('|')}")
print(c.cyan("+" + "=" * 79 + "+"))
# Items
for item in items:
title = (item.get("title", "") or "")[:35].ljust(35)
vault_name = (item.get("vault", {}).get("name", "") or "")[:20].ljust(20)
updated = (item.get("updated_at", "N/A") or "N/A")[:10].ljust(15)
print(f"{c.cyan('|')} {title} | {vault_name} | {updated} {c.cyan('|')}")
print(c.cyan("+" + "=" * 79 + "+"))
print()
print(f"Total: {len(items)} environments")
print()
print("Commands:")
print(" Show details: uv run op-env-show <name> <vault>")
print(" Export .env: uv run op-env-export <name> <vault> > .env")
def main_sync() -> None:
asyncio.run(main())
if __name__ == "__main__":
main_sync()
"""op-env-show — Display environment item details from 1Password (Python SDK version).
Usage:
op-env-show <name> <vault> [--reveal] [--json] [--keys]
"""
from __future__ import annotations
import argparse
import asyncio
import json
import sys
from .utils import (
colors,
extract_variables,
find_item_by_title,
get_client,
log,
resolve_vault_id,
)
HELP_EPILOG = """\
examples:
# Show with masked values
uv run op-env-show my-app-dev Development
# Show with revealed values
uv run op-env-show my-app-prod Production --reveal
# JSON output
uv run op-env-show my-app-dev Development --json
# List only variable names
uv run op-env-show azure-config Shared --keys
"""
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
prog="op-env-show",
description="Display environment item details from 1Password",
epilog=HELP_EPILOG,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("name", help="Name/title of the environment item")
parser.add_argument("vault", help="Vault containing the item")
parser.add_argument(
"--reveal", "-r", action="store_true", help="Show concealed values (default: masked)"
)
parser.add_argument("--json", dest="json_output", action="store_true", help="JSON output")
parser.add_argument("--keys", action="store_true", help="Show only variable names")
return parser
async def main() -> None:
parser = build_parser()
args = parser.parse_args()
client = await get_client()
vault_id = await resolve_vault_id(client, args.vault)
# Find the item
item = await find_item_by_title(client, vault_id, args.name)
if item is None:
log.error(f"Item '{args.name}' not found in vault '{args.vault}'")
sys.exit(1)
# JSON output mode — serialize the raw item
if args.json_output:
# Build a serializable representation
variables = extract_variables(item)
if args.reveal:
# Resolve actual values via secret references
revealed = {}
for key in variables:
ref = f"op://{args.vault}/{args.name}/variables/{key}"
try:
revealed[key] = await client.secrets.resolve(ref)
except Exception:
revealed[key] = variables[key]
variables = revealed
output = {
"title": getattr(item, "title", args.name),
"vault": args.vault,
"category": str(getattr(item, "category", "Unknown")),
"tags": getattr(item, "tags", []),
"variables": variables,
}
print(json.dumps(output, indent=2))
return
# Extract metadata
title = getattr(item, "title", args.name)
category = str(getattr(item, "category", "Unknown"))
tags = ", ".join(getattr(item, "tags", [])) or "none"
created = str(getattr(item, "created_at", "N/A"))
updated = str(getattr(item, "updated_at", "N/A"))
c = colors
# Display header
print()
print(c.cyan("+" + "=" * 63 + "+"))
print(f"{c.cyan('|')} Environment: {c.green(title)}")
print(c.cyan("+" + "=" * 63 + "+"))
print(f"{c.cyan('|')} Vault: {args.vault}")
print(f"{c.cyan('|')} Category: {category}")
print(f"{c.cyan('|')} Tags: {tags}")
print(f"{c.cyan('|')} Created: {created}")
print(f"{c.cyan('|')} Updated: {updated}")
print(c.cyan("+" + "=" * 63 + "+"))
print(f"{c.cyan('|')} Variables:")
print(c.cyan("+" + "=" * 63 + "+"))
print()
# Extract and display variables
variables = extract_variables(item)
if not variables:
print(" (no variables found)")
else:
for key, value in variables.items():
if args.keys:
print(f" {key}")
elif args.reveal:
# Resolve actual secret value
ref = f"op://{args.vault}/{args.name}/variables/{key}"
try:
revealed = await client.secrets.resolve(ref)
except Exception:
revealed = value
print(f" {key}={revealed}")
else:
masked = "********" if value else "(empty)"
print(f" {key}={masked}")
print()
if not args.reveal and not args.keys:
print("Tip: Use --reveal to show actual values")
def main_sync() -> None:
asyncio.run(main())
if __name__ == "__main__":
main_sync()
"""op-env-update — Update environment item in 1Password (Python SDK version).
Usage:
op-env-update <name> <vault> [--from-file <.env>] [--remove <keys>] [KEY=VALUE ...]
"""
from __future__ import annotations
import argparse
import asyncio
import os
import sys
from .utils import (
find_item_by_title,
get_client,
log,
parse_env_file,
parse_inline_vars,
resolve_vault_id,
)
HELP_EPILOG = """\
examples:
# Update single variable
uv run op-env-update my-app-dev Development API_KEY=new-key
# Merge from .env file
uv run op-env-update my-app-prod Production --from-file .env.prod
# Remove specific variables
uv run op-env-update azure-config Shared --remove OLD_KEY,DEPRECATED_VAR
# Update and remove in one command
uv run op-env-update my-app Development NEW_KEY=value --remove OLD_KEY
"""
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
prog="op-env-update",
description="Update environment item in 1Password",
epilog=HELP_EPILOG,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument("positionals", nargs="*", help="<name> <vault> [KEY=VALUE ...]")
parser.add_argument("--from-file", dest="from_file", help="Import/merge variables from .env")
parser.add_argument("--remove", help="Comma-separated list of keys to remove")
return parser
async def main() -> None:
parser = build_parser()
args = parser.parse_args()
# Separate name, vault, and KEY=VALUE from positionals
name = ""
vault = ""
env_args: list[str] = []
for arg in args.positionals:
if "=" in arg:
env_args.append(arg)
elif not name:
name = arg
elif not vault:
vault = arg
if not name or not vault:
log.error("Missing required arguments: name and vault")
parser.print_help()
sys.exit(1)
client = await get_client()
vault_id = await resolve_vault_id(client, vault)
# Find existing item
item = await find_item_by_title(client, vault_id, name)
if item is None:
log.error(f"Item '{name}' not found in vault '{vault}'")
log.info("Use op-env-create to create a new environment")
sys.exit(1)
log.info(f"Updating environment: {name} in vault: {vault}")
# Handle removals
if args.remove:
keys_to_remove = [k.strip() for k in args.remove.split(",")]
# Remove fields from the item
current_fields = list(getattr(item, "fields", []))
for key in keys_to_remove:
log.info(f"Removing variable: {key}")
current_fields = [
f for f in current_fields
if not (
(getattr(f, "title", "") == key or getattr(f, "label", "") == key)
and (
getattr(f, "section_id", "") == "variables"
or getattr(getattr(f, "section", None), "id", "") == "variables"
or str(getattr(f, "field_type", "")) == "Concealed"
)
)
]
item.fields = current_fields
# Collect variables to add/update
variables: dict[str, str] = {}
if args.from_file:
if not os.path.isfile(args.from_file):
log.error(f"File not found: {args.from_file}")
sys.exit(1)
log.info(f"Loading variables from: {args.from_file}")
with open(args.from_file) as f:
variables.update(parse_env_file(f.read()))
# Add inline variables
variables.update(parse_inline_vars(env_args))
# Apply variable updates to item fields
if variables:
log.info(f"Updating variables: {' '.join(variables.keys())}")
current_fields = list(getattr(item, "fields", []))
for key, value in variables.items():
# Check if field already exists and update it
found = False
for field in current_fields:
field_title = getattr(field, "title", "") or getattr(field, "label", "")
sec_id = getattr(field, "section_id", "") or getattr(
getattr(field, "section", None), "id", ""
)
if field_title == key and sec_id == "variables":
field.value = value
found = True
break
if not found:
from onepassword.types import ItemField, ItemFieldType
current_fields.append(
ItemField(
id=key,
title=key,
value=value,
field_type=ItemFieldType.CONCEALED,
section_id="variables",
)
)
item.fields = current_fields
# Save the updated item
if variables or args.remove:
try:
await client.items.put(vault_id, item)
log.info(f"Environment '{name}' updated successfully")
except Exception as exc:
log.error(f"Failed to update environment: {exc}")
sys.exit(1)
else:
log.warn("No variables to update")
print("\nCurrent state:")
print(f" uv run op-env-show '{name}' '{vault}'")
def main_sync() -> None:
asyncio.run(main())
if __name__ == "__main__":
main_sync()
{
"name": "@1password-skill/tools",
"version": "1.0.0",
"description": "CLI tools for managing 1Password Developer Environments",
"type": "module",
"bin": {
"op-env-create": "./src/op-env-create.ts",
"op-env-update": "./src/op-env-update.ts",
"op-env-delete": "./src/op-env-delete.ts",
"op-env-show": "./src/op-env-show.ts",
"op-env-list": "./src/op-env-list.ts",
"op-env-export": "./src/op-env-export.ts"
},
"scripts": {
"create": "bun run src/op-env-create.ts",
"update": "bun run src/op-env-update.ts",
"delete": "bun run src/op-env-delete.ts",
"show": "bun run src/op-env-show.ts",
"list": "bun run src/op-env-list.ts",
"export": "bun run src/op-env-export.ts",
"typecheck": "bun x tsc --noEmit"
},
"devDependencies": {
"@types/bun": "latest",
"typescript": "^5.0.0"
}
}
#!/usr/bin/env bun
/**
* op-env-delete - Delete environment item from 1Password
*
* Usage:
* op-env-delete <name> <vault> [--force] [--archive]
*/
import { parseArgs } from "util";
import {
log,
ensureAuth,
itemExists,
deleteEnvironment,
} from "./utils";
const HELP = `
op-env-delete - Delete environment item from 1Password
USAGE:
op-env-delete <name> <vault> [OPTIONS]
ARGUMENTS:
<name> Name/title of the environment item
<vault> Vault containing the item
OPTIONS:
--force Skip confirmation prompt
--archive Archive instead of permanent delete
--help Show this help message
EXAMPLES:
# Interactive deletion
op-env-delete my-app-dev Development
# Force delete without confirmation
op-env-delete old-config Shared --force
# Archive instead of delete
op-env-delete deprecated-env Production --archive
`;
async function main() {
const { values, positionals } = parseArgs({
args: Bun.argv.slice(2),
options: {
force: { type: "boolean", short: "f" },
archive: { type: "boolean" },
help: { type: "boolean", short: "h" },
},
allowPositionals: true,
});
if (values.help) {
console.log(HELP);
process.exit(0);
}
const [name, vault] = positionals;
if (!name || !vault) {
log.error("Missing required arguments: name and vault");
console.log(HELP);
process.exit(1);
}
await ensureAuth();
// Check if item exists
if (!(await itemExists(name, vault))) {
log.error(`Item '${name}' not found in vault '${vault}'`);
process.exit(1);
}
log.info("Environment to delete:");
console.log(` Name: ${name}`);
console.log(` Vault: ${vault}`);
console.log();
// Confirm unless force
if (!values.force) {
const action = values.archive ? "Archive" : "Permanently DELETE";
process.stdout.write(`${action} this environment? [y/N] `);
const reader = Bun.stdin.stream().getReader();
const { value } = await reader.read();
reader.releaseLock();
const response = new TextDecoder().decode(value).trim().toLowerCase();
if (response !== "y" && response !== "yes") {
log.info("Cancelled");
process.exit(0);
}
}
// Delete or archive
const success = await deleteEnvironment(name, vault, values.archive);
if (success) {
if (values.archive) {
log.info(`Environment '${name}' archived successfully`);
} else {
log.info(`Environment '${name}' deleted permanently`);
}
} else {
log.error("Failed to delete environment");
process.exit(1);
}
}
main().catch((err) => {
log.error(err.message);
process.exit(1);
});
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"types": ["bun-types"],
"strict": true,
"skipLibCheck": true,
"noEmit": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"isolatedModules": true
},
"include": ["src/**/*.ts"]
}