blumeops/CLAUDE.md
Erich Blume e6cf7e47e0
All checks were successful
Deploy Fly.io Proxy / deploy (push) Successful in 1m8s
Restrict flyio-proxy ACLs to dedicated tag:flyio-target endpoints (#126)
## Summary
- Introduce `tag:flyio-target` so services must explicitly opt in to be reachable by the fly.io proxy
- Replace broad `tag:k8s` and `tag:homelab` grants with the new tag in the ACL rule and test
- Add `tailscale.com/tags: "tag:k8s,tag:flyio-target"` annotation to docs, loki, and prometheus Ingresses
- Switch Alloy push endpoints from `*.ops.eblu.me` (Caddy) to `*.tail8d86e.ts.net` (Tailscale Ingress)
- Update docs: flyio-proxy, caddy, tailscale, forgejo (future public access + security checklist), expose-service-publicly

## Manual step (not in PR)
Update the k8s operator OAuth client in the Tailscale admin console to include `tag:flyio-target` in its scope. Without this, the operator cannot assign the new tag to Ingress proxy nodes.

## Deployment order
1. **Pulumi ACLs** — `mise run tailnet-preview && mise run tailnet-up`
2. **OAuth client** — Manual update in Tailscale admin console
3. **K8s Ingresses** — `argocd app sync apps && argocd app sync docs loki prometheus`
4. **Fly.io proxy** — `mise run fly-deploy`
5. **Verify** — `mise run services-check`, check Grafana dashboards

## Test plan
- [ ] `mise run tailnet-preview` shows clean diff
- [ ] `argocd app diff docs`, `argocd app diff loki`, `argocd app diff prometheus` show only annotation additions
- [ ] After deploy: Grafana dashboards show continued log/metric flow
- [ ] `curl -sf https://docs.eblu.me` returns 200
- [ ] `mise run services-check` passes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/126
2026-02-08 21:54:18 -08:00

3.6 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

  1. Always run mise run zk-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 - 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 <pr_number> before proceeding
  6. Add changelog fragments - docs/changelog.d/<branch>.<type>.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

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:

  1. Create branch, modify argocd/manifests/<service>/
  2. Push, then argocd app sync apps
  3. Test on branch: argocd app set <service> --revision <branch> && argocd app sync <service>
  4. 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 --vault vg6xf6vvfmoh5hqjjhlhbeoaie item get srogeebssulhtb6tnqd7ls6qey --fields password --reveal)"

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.