# 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!** **Shell:** The user's shell is **fish**. Use `$status` not `$?` for exit codes. Use fish syntax in interactive examples. ## Rules 1. **Always run `mise run ai-docs -- --style=header --color=never --decorations=always` at session start** This will refresh your context with important information you will be assumed to know and follow. 2. **Always use `--context=minikube-indri` with kubectl** (or `--context=k3s-ringtail` for ringtail services) - work contexts must never be touched 3. **Feature branches only** - checkout main, pull, create branch, commit often 4. **Create PRs via `tea pr create`** - user reviews before deploy, merges after 5. **Check PR comments with `mise run pr-comments `** before proceeding 6. **Add changelog fragments** - `docs/changelog.d/..md` Types: `feature`, `bugfix`, `infra`, `doc`, `ai`, `misc` 7. **Test before applying** - dry runs (`--check --diff`), syntax checks, `ssh indri '...'` 8. **Wait for user review before deploying** 9. **Never merge PRs or push to main without explicit request** 10. **Verify deployments** - `mise run services-check` ## Change Classification Before starting work, classify the change: | Class | Scope | Process | |-------|-------|---------| | **C0** | Quick fix, single-file, obvious | Read `ai-docs`, implement directly | | **C1** | Moderate, potential hidden complexity | Mikado method, single session, single PR | | **C2** | Complex, multi-session | Mikado method, documentation-driven, single PR | See [[agent-change-process]] for the full methodology. ## Project Structure ``` ./docs/ # documentation (Diataxis, Quartz) ./docs/changelog.d/ # towncrier fragments ./.dagger/ # dagger pipelines ./.forgejo/ # forgejo-runner actions and workflows ./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 ./fly/ # fly.io proxy for public routing ./pulumi/ # Pulumi IaC (tailnet ACLs, dns, cloud) ~/.config/{nvim,fish} # user's shell config, managed by chezmoi ~/code/personal/ # user's projects ~/code/personal/zk # user's Obsidian-sync managed zettelkasten. Potential source for reference data. ~/code/3rd/ # mirrored external projects ~/code/work # FORBIDDEN ``` Other code paths will be listed via ai-docs, this is just an overview. When you encounter wiki-links (`[[like-this]]`) it is referring to docs/ cards. ## Service Deployment ### Kubernetes (ArgoCD) Most services run in minikube on indri via ArgoCD (app-of-apps, manual sync). GPU workloads (Frigate, Mosquitto, ntfy) run on ringtail's k3s cluster, also managed by ArgoCD. **PR workflow:** 1. Create branch, modify `argocd/manifests//` 2. Push. Sync 'apps' app if service definition changed (set --revision to branch). 3. Test on branch: `argocd app set --revision && argocd app sync ` 4. After merge: `argocd app set --revision main && argocd app sync ` **Commands:** `argocd app list|get|diff|sync ` **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 ```fish mise run provision-indri # full mise run provision-indri -- --tags # 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 ```fish mise run container-list # show images/tags mise run container-release # tag and build ``` The goal is to eventually use only locally built containers in all cases, with full supply chain control via forge.ops.eblu.me repositories, mirroring source from upstream. ## Third-Party Projects Ask user to mirror on forge first, then clone to `~/code/3rd//`. ## Task Discovery ```fish mise run blumeops-tasks # fetch from Todoist, sorted by priority ``` Most tasks are stored in `./mise-tasks/`. For scripts with any logic or complexity, use uv run --script 's with explicit dependencies. Complex workflows with artifacts should become dagger pipelines. Mise tasks are for development processes and operations - tools for the user or the agent. ## Credentials Root store is 1Password. Never grab directly - use existing patterns (ansible pre_tasks, external-secrets, scripts with `op` CLI). It's ok to use `op item get` without `--reveal` to explore what secrets are available, however. Prefer `op read "op://vault/item/field"` over `op item get --fields` to avoid quoting issues with multi-line values.