infra/cli
Viktor Barzin 787ce4edfa
Some checks are pending
Build infra CLI / build (push) Waiting to run
ci/woodpecker/push/default Pipeline was successful
homelab: v0.3.1 — fix k8s db PG target (resolve CNPG primary pod, not the Service)
`k8s db <app>` (Postgres path) execed `pg-cluster-rw`, which is the CNPG
read-write SERVICE, not a pod — so kubectl exec failed with
`pods "pg-cluster-rw" not found`. The unit test only checked the plan; the verb
was never fired at live state (the gap flagged in v0.2), so it shipped broken.

Fix: the PG plan now carries a label selector (cnpg.io/instanceRole=primary)
instead of a pod name, and k8s db resolves the actual primary POD via
`kubectl get pod -l <selector>` before exec. MySQL path (real pod
mysql-standalone-0) unchanged. Live-verified both paths (psql + mysql).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 09:09:34 +00:00
..
cmd_claim.go homelab: scaffold unified CLI (registry, manifest, claim/release) in infra/cli 2026-06-18 19:12:57 +00:00
cmd_k8s.go homelab: v0.3.1 — fix k8s db PG target (resolve CNPG primary pod, not the Service) 2026-06-19 09:09:34 +00:00
cmd_memory.go homelab: add memory verb-group (v0.3.0) — direct claude-memory HTTP client 2026-06-19 05:56:25 +00:00
cmd_tf.go homelab: add tf verbs + stack/git-crypt substrate 2026-06-18 19:16:33 +00:00
cmd_tf_test.go homelab: add tf verbs + stack/git-crypt substrate 2026-06-18 19:16:33 +00:00
cmd_work.go homelab: add work verbs (start/land/clean) with a land verification gate 2026-06-18 19:24:08 +00:00
cmd_work_test.go homelab: add work verbs (start/land/clean) with a land verification gate 2026-06-18 19:24:08 +00:00
command.go homelab: scaffold unified CLI (registry, manifest, claim/release) in infra/cli 2026-06-18 19:12:57 +00:00
command_test.go homelab: scaffold unified CLI (registry, manifest, claim/release) in infra/cli 2026-06-18 19:12:57 +00:00
Dockerfile fix: restore tree dropped by 6d224861; land stem95su gdrive-sync (10m) [ci skip] 2026-06-09 08:45:33 +00:00
email_alias.go fix: restore tree dropped by 6d224861; land stem95su gdrive-sync (10m) [ci skip] 2026-06-09 08:45:33 +00:00
git.go fix: restore tree dropped by 6d224861; land stem95su gdrive-sync (10m) [ci skip] 2026-06-09 08:45:33 +00:00
go.mod fix: restore tree dropped by 6d224861; land stem95su gdrive-sync (10m) [ci skip] 2026-06-09 08:45:33 +00:00
go.sum fix: restore tree dropped by 6d224861; land stem95su gdrive-sync (10m) [ci skip] 2026-06-09 08:45:33 +00:00
homelab.go homelab: add memory verb-group (v0.3.0) — direct claude-memory HTTP client 2026-06-19 05:56:25 +00:00
k8s.go homelab: v0.3.1 — fix k8s db PG target (resolve CNPG primary pod, not the Service) 2026-06-19 09:09:34 +00:00
k8s_test.go homelab: v0.3.1 — fix k8s db PG target (resolve CNPG primary pod, not the Service) 2026-06-19 09:09:34 +00:00
main.go homelab: scaffold unified CLI (registry, manifest, claim/release) in infra/cli 2026-06-18 19:12:57 +00:00
memory.go homelab: add memory verb-group (v0.3.0) — direct claude-memory HTTP client 2026-06-19 05:56:25 +00:00
memory_test.go homelab: add memory verb-group (v0.3.0) — direct claude-memory HTTP client 2026-06-19 05:56:25 +00:00
openwrt_dns.go fix: restore tree dropped by 6d224861; land stem95su gdrive-sync (10m) [ci skip] 2026-06-09 08:45:33 +00:00
presence.go homelab: scaffold unified CLI (registry, manifest, claim/release) in infra/cli 2026-06-18 19:12:57 +00:00
presence_test.go homelab: scaffold unified CLI (registry, manifest, claim/release) in infra/cli 2026-06-18 19:12:57 +00:00
README.md homelab: add memory verb-group (v0.3.0) — direct claude-memory HTTP client 2026-06-19 05:56:25 +00:00
repo.go homelab: add work verbs (start/land/clean) with a land verification gate 2026-06-18 19:24:08 +00:00
repo_test.go homelab: add tf verbs + stack/git-crypt substrate 2026-06-18 19:16:33 +00:00
run.go homelab: add tf verbs + stack/git-crypt substrate 2026-06-18 19:16:33 +00:00
stack.go homelab: add tf verbs + stack/git-crypt substrate 2026-06-18 19:16:33 +00:00
stack_test.go homelab: add tf verbs + stack/git-crypt substrate 2026-06-18 19:16:33 +00:00
update_viktorbarzin_me.go homelab: scaffold unified CLI (registry, manifest, claim/release) in infra/cli 2026-06-18 19:12:57 +00:00
update_viktorbarzin_me_technitium.go fix: restore tree dropped by 6d224861; land stem95su gdrive-sync (10m) [ci skip] 2026-06-09 08:45:33 +00:00
VERSION homelab: v0.3.1 — fix k8s db PG target (resolve CNPG primary pod, not the Service) 2026-06-19 09:09:34 +00:00
vpn.go fix: restore tree dropped by 6d224861; land stem95su gdrive-sync (10m) [ci skip] 2026-06-09 08:45:33 +00:00

homelab

homelab is the unified, agent-facing CLI for operating this homelab — one composable, JSON-capable surface for the operations agents run over and over, discovered progressively at runtime. It is grown in place from this directory (the former infra-cli), and the legacy webhook use-cases still work (see below).

It encodes actions, never judgment: methodology (debugging, TDD, review) and third-party/owned MCP servers (e.g. phpIPAM) are deliberately out of scope.

Usage

homelab <command> [args]
homelab manifest [--json]    # list every verb + its read/write tier (discovery entrypoint)
homelab version

v0.1 verbs — the infra inner-loop

Command Tier What it does
claim <kind>:<name> --purpose "…" write claim a shared resource on the presence board (wraps scripts/presence)
release <kind>:<name> write release a presence claim
tf plan <stack> read scripts/tg plan for a stack (resolved from cwd)
tf validate <stack> read scripts/tg validate
tf fmt <stack> read terraform fmt -recursive on the stack
tf force-unlock <stack> <lock-id> write release a stuck state lock
tf apply <stack> write scripts/tg apply — auto-claims stack:<name>, always releases, warns it's out-of-band
work start <topic> write create .worktrees/<topic> on <user>/<topic> off <remote>/master; enter with native EnterWorktree
work land [--verify-cmd "…"] [--no-verify] write merge master in → verify → push HEAD:master (non-ff retry; PR fallback)
work clean <topic> write remove a task's worktree + branch (run from the main checkout)

v0.2 verbs — Kubernetes

Built on an app→namespace→pod resolver: <app> defaults to the namespace (most namespaces hold one app); the target defaults to deploy/<app> and lets kubectl resolve the pod. Override with -n/--pod/-c/-l/--tty. Uses the ambient kubeconfig.

Command Tier What it does
k8s status [ns] read pods (wide) + recent non-Normal events (-A if no ns)
k8s get <ns> <resource> […] read kubectl -n <ns> get … passthrough
k8s logs <app> read logs for deploy/<app> (--tail default 200; -c/--previous/--since/-l)
k8s describe <app> [resource] read describe the deployment (or an explicit resource)
k8s debug <app> read one-shot triage: pods + workloads + describe + recent logs + events
k8s pf <app> <local:remote> [target] read port-forward to svc/<app> (or an explicit target)
k8s rollout-status <app> read rollout status deploy/<app>
k8s db <app> [--mysql] [--db N] -- "<SQL>" write exec into the dbaas DB (PG pg-cluster-rw, or MySQL with env-password wrapper)
k8s exec <app> [--tty] -- <cmd> write exec in the app's pod
k8s restart <app> write rollout restart deploy/<app> then wait for status
k8s rm-pod <name> -n <ns> [--job] [--force] write delete a stuck pod/job only

Config-mutation verbs (apply/edit/patch/scale/create) are intentionally not exposed — they stay raw kubectl, per the Terraform-only policy.

tf resolves the stack dir by walking up from cwd to the infra root and delegates to scripts/tg (which owns state decrypt/encrypt, the Vault lock, and the ingress auth-comment check). git-crypt filter flags are auto-injected on git operations in the encrypted infra repo.

work land refuses to push when it cannot verify (no --verify-cmd and no auto-detected suite) unless you pass --no-verify — landing to master unverified must be deliberate. It does not yet block on CI to green (that arrives with the ci/deploy watch verbs); it reminds you to follow the pipeline.

Tiers are recorded per verb so a future PreToolUse classifier can auto-allow reads / prompt writes; v0.1 allows everything and relies on existing gates (permission mode, presence claims, plan approval).

v0.3 verbs — memory

A thin HTTP client over the claude-memory service (the same backend the memory MCP wraps), authed with CLAUDE_MEMORY_API_KEY against CLAUDE_MEMORY_API_URL (the env the hooks already set; defaults to the ingress). Because it hits the HTTP API directly, it works even when the MCP frontend is down.

Command Tier What it does
memory recall "<context>" [--query --category --sort --limit] read semantic search (server-side ranking) — the navigate workhorse
memory list [--category --tag --limit] read recent memories
memory categories / memory tags / memory stats read enumerate the store
memory secret <id> read reveal a sensitive memory's content
memory store "<content>" [--category --tags --keywords --importance --sensitive] write store a memory
memory update <id> [--content --tags --importance] write edit a memory
memory delete <id> write delete a memory

All read/write paths are validated against the live API (incl. a store→recall→delete round-trip). This gives full data-plane parity with the MCP; the eventual deprecation (rewiring the per-prompt auto-recall + auto-learn hooks to the CLI, then uninstalling the MCP) is a separate, deliberate follow-up — see docs/adr/0008.

Build / install

Built from source to /usr/local/bin/homelab during devvm provisioning (scripts/workstation/setup-devvm.sh, the t3-dispatch pattern); version is stamped from cli/VERSION via ldflags. Manual build:

cd cli && go build -ldflags "-X main.version=$(cat VERSION)" -o /usr/local/bin/homelab .
go test ./...

Legacy webhook use-cases (preserved)

This binary is also the in-cluster infra-cli image. Invocations starting with -use-case=<vpn|setup-openwrt-dns|add-email-alias|...> fall through to the original flag-based path unchanged, so the webhook handler is unaffected.

Design

See infra/docs/adr/00040008 for the architecture decisions.