All checks were successful
Build Container / build (push) Successful in 6m28s
## Summary - Add `containers/ntfy/Dockerfile` — three-stage build (Node web UI, Go+CGO server, Alpine runtime) pinned to commit SHA `a03a37fe` (v2.17.0), sourced from forge mirror - Update ntfy deployment image from `binwiederhier/ntfy:v2.17.0` to `registry.ops.eblu.me/blumeops/ntfy:v1.0.0` - Note fish shell in CLAUDE.md ## Deployment After merge, release the container image: ```fish mise run container-tag-and-release ntfy v1.0.0 ``` Then sync: ```fish argocd app sync ntfy ``` ## Test plan - [x] `docker build` succeeds - [x] `dagger call build --src=. --container-name=ntfy` succeeds (exit 0, container ID printed) - [x] `ntfy --help` works in built container - [ ] Tag and release `ntfy-v1.0.0` after merge - [ ] Verify ntfy pod starts with new image - [ ] Verify health endpoint responds at `ntfy.ops.eblu.me/v1/health` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/202
118 lines
4.8 KiB
Markdown
118 lines
4.8 KiB
Markdown
# 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 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
|
|
./.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 zk-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).
|
|
|
|
**PR workflow:**
|
|
1. Create branch, modify `argocd/manifests/<service>/`
|
|
2. Push. Sync 'apps' app if service definition changed (set --revision to branch).
|
|
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 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 <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
|
|
|
|
```fish
|
|
mise run container-list # show images/tags
|
|
mise run container-release <name> <version> # 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/<project>/`.
|
|
|
|
## 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.
|