Session log: 1Password skill setup, Lonestar MDM fix, credentials migration planning
- Activated 1Password skill for Claude Code (extracted from .skill ZIP) - Resolved Lonestar Electrical MDM issue: ManageEngine was configured as third-party EMM in Google Workspace, causing persistent enrollment prompts on joser's personal phone - Scoped credentials.md migration to 1Password (op:// refs + MSP vaults) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
222
.claude/skills/1password/references/integrations.md
Normal file
222
.claude/skills/1password/references/integrations.md
Normal file
@@ -0,0 +1,222 @@
|
||||
# 1Password Integration Patterns
|
||||
|
||||
Common patterns for integrating 1Password with developer tools and AI workflows.
|
||||
|
||||
## Claude Code / Claude Desktop
|
||||
|
||||
### Claude Desktop MCP Config
|
||||
|
||||
Store API keys securely and reference them in `claude_desktop_config.json`:
|
||||
|
||||
```bash
|
||||
# Store the key
|
||||
op item create --category API_CREDENTIAL --title "My MCP Server" \
|
||||
--vault Dev api_key[password]=your-key-here
|
||||
|
||||
# Get the secret reference
|
||||
# op://Dev/My MCP Server/api_key
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"my-server": {
|
||||
"command": "op",
|
||||
"args": ["run", "--", "node", "/path/to/server.js"],
|
||||
"env": {
|
||||
"API_KEY": "op://Dev/My MCP Server/api_key"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Claude Code Shell Environment
|
||||
|
||||
```bash
|
||||
# .env.tpl (safe to commit — no real secrets)
|
||||
ANTHROPIC_API_KEY=op://Dev/Anthropic/api_key
|
||||
OPENAI_API_KEY=op://Dev/OpenAI/api_key
|
||||
|
||||
# ✅ Wrap claude with op run — secrets injected into subprocess only
|
||||
op run --env-file=.env.tpl -- claude
|
||||
|
||||
# ✅ Or export individually for interactive shell use
|
||||
export ANTHROPIC_API_KEY=$(op read "op://Dev/Anthropic/api_key")
|
||||
claude
|
||||
```
|
||||
|
||||
### In CLAUDE.md (project secrets reference)
|
||||
|
||||
```markdown
|
||||
## Secrets Setup
|
||||
Secrets are managed via 1Password. Run before working:
|
||||
```bash
|
||||
op run --env-file=.env.tpl -- claude
|
||||
```
|
||||
Do NOT commit `.env` — commit `.env.tpl` only.
|
||||
```
|
||||
|
||||
## n8n
|
||||
|
||||
### Environment Injection at Startup
|
||||
|
||||
```bash
|
||||
# n8n.env.tpl (commit this)
|
||||
N8N_ENCRYPTION_KEY=op://Dev/n8n/encryption_key
|
||||
DB_POSTGRESDB_PASSWORD=op://Dev/n8n-postgres/password
|
||||
N8N_BASIC_AUTH_PASSWORD=op://Dev/n8n/basic_auth_password
|
||||
|
||||
# docker-compose.yml startup
|
||||
op run --env-file=n8n.env.tpl -- docker compose up -d n8n
|
||||
```
|
||||
|
||||
### n8n Credential Storage via API
|
||||
|
||||
Use n8n's credential API to push secrets from 1Password into n8n:
|
||||
|
||||
```bash
|
||||
# Get secret from 1Password
|
||||
API_KEY=$(op read "op://Dev/Some Service/api_key")
|
||||
|
||||
# Push to n8n credential (HTTP Request)
|
||||
curl -s -X POST "https://n8n.example.com/api/v1/credentials" \
|
||||
-H "X-N8N-API-KEY: $(op read 'op://Dev/n8n/api_key')" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"name\": \"Service Credential\", \"type\": \"httpHeaderAuth\", \"data\": {\"name\": \"Authorization\", \"value\": \"Bearer $API_KEY\"}}"
|
||||
```
|
||||
|
||||
## Docker / Docker Compose
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
services:
|
||||
app:
|
||||
image: myapp:latest
|
||||
environment:
|
||||
DATABASE_URL: ${DATABASE_URL}
|
||||
API_KEY: ${API_KEY}
|
||||
```
|
||||
|
||||
```bash
|
||||
# .env.tpl
|
||||
DATABASE_URL=op://Dev/Postgres/connection_string
|
||||
API_KEY=op://Dev/MyApp/api_key
|
||||
|
||||
# Start with injection
|
||||
op run --env-file=.env.tpl -- docker compose up
|
||||
```
|
||||
|
||||
## Python Scripts
|
||||
|
||||
```python
|
||||
import subprocess
|
||||
|
||||
def get_secret(reference: str) -> str:
|
||||
"""Read a secret from 1Password using a secret reference."""
|
||||
result = subprocess.run(
|
||||
["op", "read", reference],
|
||||
capture_output=True, text=True, check=True
|
||||
)
|
||||
return result.stdout.strip()
|
||||
|
||||
# Usage
|
||||
api_key = get_secret("op://Dev/Anthropic/api_key")
|
||||
```
|
||||
|
||||
Or using the 1Password Python SDK (if available):
|
||||
```bash
|
||||
pip install onepassword-sdk
|
||||
```
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
import onepassword
|
||||
|
||||
async def main():
|
||||
client = await onepassword.Client.authenticate(
|
||||
auth=os.environ["OP_SERVICE_ACCOUNT_TOKEN"],
|
||||
integration_name="My Script",
|
||||
integration_version="1.0.0",
|
||||
)
|
||||
secret = await client.secrets.resolve("op://Dev/Anthropic/api_key")
|
||||
```
|
||||
|
||||
## GitHub Actions / CI
|
||||
|
||||
```yaml
|
||||
# .github/workflows/deploy.yml
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: 1password/load-secrets-action@v2
|
||||
with:
|
||||
export-env: true
|
||||
env:
|
||||
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
|
||||
ANTHROPIC_API_KEY: op://Dev/Anthropic/api_key
|
||||
DEPLOY_KEY: op://Dev/Deploy/private_key
|
||||
|
||||
- run: deploy-script.sh # ANTHROPIC_API_KEY is available
|
||||
```
|
||||
|
||||
## Shell / .zshrc Auto-Load
|
||||
|
||||
```bash
|
||||
# ~/.zshrc
|
||||
# Auto-load common dev secrets on shell start (optional — only if you trust your machine)
|
||||
load_dev_secrets() {
|
||||
if command -v op &>/dev/null && op whoami &>/dev/null 2>&1; then
|
||||
source <(op run --env-file=~/.config/dev.env.tpl -- env 2>/dev/null) && \
|
||||
echo "✅ Dev secrets loaded from 1Password"
|
||||
fi
|
||||
}
|
||||
|
||||
# Call explicitly when needed:
|
||||
alias load-secrets='load_dev_secrets'
|
||||
```
|
||||
|
||||
## Supabase
|
||||
|
||||
```bash
|
||||
# Store Supabase credentials
|
||||
op item create --category API_CREDENTIAL --title "Supabase - My Project" \
|
||||
--vault Dev \
|
||||
url[text]=https://myproject.supabase.co \
|
||||
anon_key[password]=eyJ... \
|
||||
service_key[password]=eyJ...
|
||||
|
||||
# Use in scripts
|
||||
SUPABASE_URL=$(op read "op://Dev/Supabase - My Project/url")
|
||||
SUPABASE_KEY=$(op read "op://Dev/Supabase - My Project/service_key")
|
||||
```
|
||||
|
||||
## Replit
|
||||
|
||||
Replit has its own Secrets manager, but for local dev before deploying:
|
||||
|
||||
```bash
|
||||
# Generate a .env from 1Password, then paste values into Replit Secrets UI
|
||||
op run --env-file=.env.tpl -- env | grep -E "^(ANTHROPIC|SUPABASE|N8N)"
|
||||
# Copy output values → paste into Replit Secrets one by one
|
||||
```
|
||||
|
||||
## Rotation Workflow
|
||||
|
||||
When rotating a credential:
|
||||
|
||||
```bash
|
||||
# 1. Update in the service (get new key)
|
||||
NEW_KEY="new-key-from-service"
|
||||
|
||||
# 2. Update in 1Password
|
||||
op item edit "Service Name" api_key[password]="$NEW_KEY"
|
||||
|
||||
# 3. Verify
|
||||
op read "op://Dev/Service Name/api_key"
|
||||
|
||||
# 4. Re-inject wherever used
|
||||
source <(op run --env-file=.env.tpl -- env)
|
||||
# Or restart services that use the key
|
||||
```
|
||||
171
.claude/skills/1password/references/op_commands.md
Normal file
171
.claude/skills/1password/references/op_commands.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# 1Password CLI (op) Command Reference
|
||||
|
||||
## Authentication
|
||||
|
||||
```bash
|
||||
# Sign in (interactive)
|
||||
op signin
|
||||
|
||||
# Sign in to specific account
|
||||
op signin --account team-name.1password.com
|
||||
|
||||
# Check who you're signed in as
|
||||
op whoami
|
||||
|
||||
# List accounts
|
||||
op account list
|
||||
|
||||
# Service account (CI/CD — set env var, no signin needed)
|
||||
export OP_SERVICE_ACCOUNT_TOKEN="your-token"
|
||||
```
|
||||
|
||||
## Items
|
||||
|
||||
```bash
|
||||
# List items
|
||||
op item list
|
||||
op item list --vault Dev
|
||||
op item list --categories API_CREDENTIAL
|
||||
|
||||
# Get item details
|
||||
op item get "Item Title"
|
||||
op item get "Item Title" --vault Dev
|
||||
op item get "Item Title" --format json
|
||||
|
||||
# Get a specific field
|
||||
op item get "Item Title" --fields api_key
|
||||
op item get "Item Title" --fields label=api_key
|
||||
|
||||
# Read using secret reference (most common)
|
||||
op read "op://Dev/Item Title/api_key"
|
||||
|
||||
# Create item
|
||||
op item create --category API_CREDENTIAL --title "My API Key" api_key[password]=sk-abc123
|
||||
op item create --category LOGIN --title "Service Account" --vault Dev \
|
||||
username[text]=myuser password[password]=mypass
|
||||
|
||||
# Edit/update item
|
||||
op item edit "Item Title" api_key[password]=new-value
|
||||
op item edit "Item Title" --vault Dev new_field[text]=value
|
||||
|
||||
# Delete item
|
||||
op item delete "Item Title"
|
||||
op item delete "Item Title" --vault Dev
|
||||
|
||||
# Move item to different vault
|
||||
op item move "Item Title" --current-vault Dev --destination-vault Personal
|
||||
```
|
||||
|
||||
## Vaults
|
||||
|
||||
```bash
|
||||
# List vaults
|
||||
op vault list
|
||||
op vault list --format json
|
||||
|
||||
# Create vault
|
||||
op vault create "New Vault"
|
||||
|
||||
# Get vault details
|
||||
op vault get "Vault Name"
|
||||
```
|
||||
|
||||
## Secrets Injection
|
||||
|
||||
```bash
|
||||
# Run command with secrets from .env template (RECOMMENDED)
|
||||
op run --env-file=.env.tpl -- your-command arg1 arg2
|
||||
|
||||
# Inject into Docker
|
||||
op run --env-file=.env.tpl -- docker compose up
|
||||
|
||||
# Inject a single reference via env var (op run picks up op:// values automatically)
|
||||
export API_KEY="op://Dev/MyApp/api_key"
|
||||
op run -- node app.js # API_KEY is resolved at runtime
|
||||
|
||||
# ⚠️ AVOID: sourcing op run output into the current shell
|
||||
# source <(op run --env-file=.env.tpl -- env) ← UNSAFE
|
||||
# If secret values contain $(...) or backticks, they execute as shell code.
|
||||
# Use 'op run -- your-command' instead (secrets stay in subprocess only).
|
||||
```
|
||||
|
||||
## Password Generation
|
||||
|
||||
```bash
|
||||
# Generate at item creation time (no standalone command)
|
||||
op item create --category PASSWORD --title "Generated Secret" \
|
||||
--generate-password='letters,digits,symbols,32'
|
||||
|
||||
# Generate with custom recipe
|
||||
op item create --category LOGIN --title "My Login" \
|
||||
--generate-password='letters,digits,20'
|
||||
|
||||
# Or use openssl for scripted generation
|
||||
openssl rand -base64 32 | tr -d '=+/'
|
||||
```
|
||||
|
||||
## Document / File Management
|
||||
|
||||
```bash
|
||||
# Store a file
|
||||
op document create ./private-key.pem --title "SSH Private Key" --vault Dev
|
||||
|
||||
# Get a file
|
||||
op document get "SSH Private Key" --output ./private-key.pem
|
||||
|
||||
# List documents
|
||||
op document list
|
||||
```
|
||||
|
||||
## Service Accounts (CI/CD)
|
||||
|
||||
```bash
|
||||
# Create service account (in 1Password UI: Settings → Developer → Service Accounts)
|
||||
# Then set token as env var:
|
||||
export OP_SERVICE_ACCOUNT_TOKEN="ops_eyJ..."
|
||||
|
||||
# No signin needed — op commands work automatically
|
||||
op item list # works with service account token
|
||||
op read "op://vault/item/field"
|
||||
```
|
||||
|
||||
## Connect (Self-hosted, advanced)
|
||||
|
||||
```bash
|
||||
# For teams running 1Password Connect server
|
||||
export OP_CONNECT_HOST="https://your-connect-server"
|
||||
export OP_CONNECT_TOKEN="your-connect-token"
|
||||
|
||||
# Then op commands use Connect instead of 1Password.com
|
||||
op item get "Item Title"
|
||||
```
|
||||
|
||||
## Output Formats
|
||||
|
||||
Valid values: `json` or `human-readable` (default).
|
||||
|
||||
```bash
|
||||
op item list --format=json # Machine-readable JSON
|
||||
op item get "Item" --format=json # Full item JSON
|
||||
op item list # Human-readable (default)
|
||||
op vault list --format=json # Vaults as JSON
|
||||
```
|
||||
|
||||
## Useful Patterns
|
||||
|
||||
```bash
|
||||
# Find item by field value (search)
|
||||
op item list --format=json | \
|
||||
python3 -c "import sys,json; [print(i['title']) for i in json.load(sys.stdin)]"
|
||||
|
||||
# Export all items in a vault to JSON (backup)
|
||||
op item list --vault Dev --format=json | \
|
||||
python3 -c "import sys,json; ids=[i['id'] for i in json.load(sys.stdin)]"
|
||||
# (then loop to get each)
|
||||
|
||||
# Check if a specific item exists
|
||||
op item get "My Item" &>/dev/null && echo "exists" || echo "not found"
|
||||
|
||||
# Get item ID (for scripting)
|
||||
op item get "My Item" --format=json | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])"
|
||||
```
|
||||
120
.claude/skills/1password/references/secret_references.md
Normal file
120
.claude/skills/1password/references/secret_references.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# 1Password Secret References
|
||||
|
||||
Secret references are the safest way to use secrets — they point to 1Password without exposing actual values in code or config files.
|
||||
|
||||
## Syntax
|
||||
|
||||
```
|
||||
op://vault/item/field
|
||||
op://vault/item/section/field
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
op://Dev/Anthropic/api_key
|
||||
op://Personal/AWS/access_key_id
|
||||
op://Dev/Supabase/section/service_key
|
||||
```
|
||||
|
||||
## Reading a Secret Reference
|
||||
|
||||
```bash
|
||||
# Single secret
|
||||
op read "op://Dev/Anthropic/api_key"
|
||||
|
||||
# Into a variable
|
||||
export ANTHROPIC_API_KEY=$(op read "op://Dev/Anthropic/api_key")
|
||||
|
||||
# Multiple secrets via op run
|
||||
op run --env-file=.env.tpl -- your-command
|
||||
```
|
||||
|
||||
## .env Template Files
|
||||
|
||||
Store references in a `.env.tpl` file (safe to commit to **private** repos):
|
||||
|
||||
> **Privacy note:** `.env.tpl` contains your vault names, item names, and field names —
|
||||
> e.g. `op://Dev/Anthropic/api_key`. This reveals the structure of your 1Password vault
|
||||
> to anyone who can read the file. For **private repos**, this is fine. For **public repos**,
|
||||
> consider whether your vault/item naming reveals anything sensitive (client names, internal
|
||||
> service names, etc.). Real secret values are never exposed — only the structure.
|
||||
|
||||
```bash
|
||||
# .env.tpl — commit this
|
||||
ANTHROPIC_API_KEY=op://Dev/Anthropic/api_key
|
||||
N8N_API_KEY=op://Dev/n8n/api_key
|
||||
SUPABASE_SERVICE_KEY=op://Dev/Supabase/service_key
|
||||
NOTION_TOKEN=op://Dev/Notion/api_token
|
||||
```
|
||||
|
||||
Then inject at runtime:
|
||||
```bash
|
||||
# ✅ RECOMMENDED — run your command with secrets injected into subprocess only
|
||||
op run --env-file=.env.tpl -- npm start
|
||||
op run --env-file=.env.tpl -- node server.js
|
||||
op run --env-file=.env.tpl -- docker compose up
|
||||
|
||||
# ✅ OK — read a single secret into a variable for immediate use
|
||||
export ANTHROPIC_API_KEY=$(op read "op://Dev/Anthropic/api_key")
|
||||
|
||||
# ⚠️ AVOID — sourcing op run output exposes secrets in current shell
|
||||
# and is unsafe if any secret value contains shell metacharacters like $(...):
|
||||
# source <(op run --env-file=.env.tpl -- env) ← DON'T DO THIS
|
||||
|
||||
# ⚠️ AVOID — writing resolved secrets to disk (don't commit .env)
|
||||
# op run --env-file=.env.tpl -- env > .env ← only if truly necessary
|
||||
```
|
||||
|
||||
## In Config Files
|
||||
|
||||
Claude Desktop (`claude_desktop_config.json`):
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"my-server": {
|
||||
"command": "op",
|
||||
"args": ["run", "--", "node", "server.js"],
|
||||
"env": {
|
||||
"API_KEY": "op://Dev/MyServer/api_key"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Docker Compose:
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
image: myapp
|
||||
environment:
|
||||
- DATABASE_URL=op://Dev/Postgres/connection_string
|
||||
```
|
||||
Run with: `op run -- docker compose up`
|
||||
|
||||
n8n (environment injection):
|
||||
```bash
|
||||
# In your n8n startup script
|
||||
op run --env-file=n8n.env.tpl -- docker compose up n8n
|
||||
```
|
||||
|
||||
## Finding Field Names
|
||||
|
||||
```bash
|
||||
# List all fields in an item
|
||||
op item get "Item Name" --format=json | \
|
||||
python3 -c "import sys,json; [print(f['label']) for f in json.load(sys.stdin)['fields'] if f.get('value')]"
|
||||
|
||||
# Or view interactively
|
||||
op item get "Item Name"
|
||||
```
|
||||
|
||||
## Common Field Names by Category
|
||||
|
||||
| Category | Common Fields |
|
||||
|----------|---------------|
|
||||
| API_CREDENTIAL | `api_key`, `credential`, `token` |
|
||||
| LOGIN | `username`, `password` |
|
||||
| DATABASE | `connection_string`, `host`, `port`, `username`, `password` |
|
||||
| SECURE_NOTE | `notesPlain` |
|
||||
| SERVER | `hostname`, `port`, `username`, `password` |
|
||||
Reference in New Issue
Block a user