## Summary - Adds guidance to CLAUDE.md: use `op read` for secret values, `op item get` only for metadata - Fixes the argocd login example which used `op item get --fields` - `op item get --fields` wraps multi-line values in quotes, which corrupts keys and other secrets Discovered while verifying the sifaka borg repokey in 1Password — hashes didn't match until we switched to `op read`. Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/143
4 KiB
CLAUDE.md
Guidance for Claude Code working in this repository. See also ai-assistance-guide.
Overview
blumeops is Erich Blume's GitOps repository for personal infrastructure, orchestrated via tailnet tail8d86e.ts.net.
CRITICAL: Public repo at github.com/eblume/blumeops - never commit secrets!
Rules
- Always run
mise run zk-docs -- --style=header --color=never --decorations=alwaysat session start This will refresh your context with important information you will be assumed to know and follow. - Always use
--context=minikube-indriwith kubectl - work contexts must never be touched - Feature branches only - checkout main, pull, create branch, commit often
- Create PRs via
tea pr create- user reviews before deploy, merges after - Check PR comments with
mise run pr-comments <pr_number>before proceeding - Add changelog fragments -
docs/changelog.d/<branch>.<type>.mdTypes:feature,bugfix,infra,doc,ai,misc - Test before applying - dry runs (
--check --diff), syntax checks,ssh indri '...' - Wait for user review before deploying
- Never merge PRs or push to main without explicit request
- Verify deployments -
mise run services-check
Project Structure
./docs/ # documentation (Diataxis, Quartz)
./docs/changelog.d/ # towncrier fragments
./mise-tasks/ # scripts via `mise run`
./ansible/playbooks/ # ansible (indri.yml primary)
./ansible/roles/ # indri service roles
./argocd/apps/ # ArgoCD Application definitions
./argocd/manifests/ # k8s manifests per service
./pulumi/ # Pulumi IaC (tailnet ACLs, cloud)
~/code/personal/ # user's projects
~/code/3rd/ # mirrored external projects
~/code/work # FORBIDDEN
Service Deployment
Kubernetes (ArgoCD)
Most services run in minikube on indri via ArgoCD (app-of-apps, manual sync).
PR workflow:
- Create branch, modify
argocd/manifests/<service>/ - Push, then
argocd app sync apps - Test on branch:
argocd app set <service> --revision <branch> && argocd app sync <service> - After merge:
argocd app set <service> --revision main && argocd app sync <service>
Commands: argocd app list|get|diff|sync <app>
Login: argocd login argocd.ops.eblu.me --username admin --password "$(op read 'op://vg6xf6vvfmoh5hqjjhlhbeoaie/srogeebssulhtb6tnqd7ls6qey/password')"
Indri (Ansible)
Native services: Forgejo, Zot, Caddy, Borgmatic, Alloy
mise run provision-indri # full
mise run provision-indri -- --tags <role> # specific
mise run provision-indri -- --check --diff # dry run
Routing
| Domain | Mechanism | Reachable from |
|---|---|---|
*.eblu.me |
Fly.io proxy (Tailscale tunnel) | public internet |
*.ops.eblu.me |
Caddy on indri | k8s pods, containers, tailnet |
*.tail8d86e.ts.net |
Tailscale MagicDNS | tailnet clients only |
Check tailscale serve: ssh indri 'tailscale serve status --json'
Container Releases
mise run container-list # show images/tags
mise run container-release <name> <version> # tag and build
Third-Party Projects
Ask user to mirror on forge first, then clone to ~/code/3rd/<project>/.
Task Discovery
mise run blumeops-tasks # fetch from Todoist, sorted by priority
Credentials
Root store is 1Password. Never grab directly - use existing patterns (ansible pre_tasks, external-secrets, scripts with op CLI). Warn user before any credential access.
op read vs op item get: Always use op read "op://vault/item/field" to retrieve secret values. op item get --fields wraps multi-line values in quotes, corrupting them. Use op item get only for listing item metadata (title, vault, field names), never for reading actual secret values in scripts or IaC. Look for existing uses of op item get --fields in Ansible/scripts and suggest replacing with op read.