4.4 KiB
Plan: Migrate bin/homelab CLI to Go oikos homelab
Status: Planned (2026-07-07)
Goal
Replace bin/homelab (265-line Python CLI) with the Go oikos homelab
subcommand already in cmd/oikos/main.go. The Python CLI imports the
now-deleted Python kernel modules (oikos.approve, oikos.decide, etc.)
and reads from /opt/homelab-context/inventory.yaml — it's disconnected
from the new DB-backed Go stack. Once the Go CLI matches the Python one,
bin/homelab becomes ln -s "$(which oikos)" bin/homelab and the oikos
binary resolves its role from os.Args[0].
Current state
| Layer | Python | Go | Gap |
|---|---|---|---|
list |
inventory.yaml scan | DB-backed list_entities |
✅ done |
whoami |
hostname → inventory lookup | hostname() → DB query |
✅ done |
ssh |
resolve mesh IP → ssh |
same | ✅ done |
secret |
sops -d wrapper |
secret list/migrate/export-sops |
✅ done (Infisical) |
Subcommand migration plan
Phase 1 — Operational commands (P0, this week)
Core CLI ops every homelab operator uses daily.
| Subcommand | Risk | Notes |
|---|---|---|
pct |
low | SSH to Proxmox host + pct exec/list. Currently proxies via SSH. Trivial Go wrapper. |
logs |
low | journalctl SSH wrapper. Reuse ssh path from existing Go code. |
restart |
low | systemctl restart SSH wrapper. |
status |
medium | Ping + HTTP health check of every host/service. Use DB entity data + scheduler health checks. |
ssh-config |
low | Generate ~/.ssh/config.d/homelab from DB hosts list. Output-only. |
open |
low | Open a service's URL via open (macOS) / xdg-open (Linux). |
Phase 2 — Agentic layer (P1, next 2 weeks)
Oikos agentic commands that already have Go equivalents in internal/.
| Subcommand | Risk | Notes |
|---|---|---|
doctor |
low | Health check enrollment: verify age key, homelab-context clone, mesh connectivity. |
service |
medium | Service Console: explain/health/docs/log/actions/history. DB-backed — get_entity_knowledge() + tail_log(). |
decide |
low | Policy classification. Go: internal/policy/classify.go already exists. |
approval |
low | Approval requests. Go: internal/domain/approval.go exists. |
signal |
medium | Attention layer. Go: internal/domain/signal.go exists. |
node |
low | Ontology queries. Go: internal/ontology/ exists. |
change |
medium | Mutation workflow. Combine decide + approval + actuator execution log. |
Phase 3 — Infrastructure management (P2, 2-4 weeks)
| Subcommand | Risk | Notes |
|---|---|---|
render-vps-configs |
medium | Renders vps/*.tmpl with Infisical secrets. Needs template engine + SSH to VPS. |
sync |
low | Trigger homelab-context sync via launchd/systemd timer. |
apt-audit |
low | dpkg audit per host. SSH wrapper. |
apt-upgrade |
medium | apt update/upgrade in detached screen. Needs confirmation. |
Phase 4 — Cleanup (P3, when ready)
| Action | Risk | Notes |
|---|---|---|
refresh-creds |
low | Drop. Infisical handles secrets now. |
nuke |
destructive | Port or drop. Rarely used; destructive — keep as manual SSH. |
client add/remove |
medium | Client enrollment. Needs secrets-issuance replacement (manual age-keygen now). |
mcp |
low | MCP tool wrapper. Could call the Go MCP client library. |
Remove bin/homelab |
high | Replace with symlink oikos → bin/homelab. The oikos binary checks os.Args[0] for the homelab role. |
Remove bin/oikos |
low | Stale pre-built arm64 binary. Delete. |
Implementation approach
Each subcommand is a separate file in cmd/oikos/homelab/. The dispatcher
in main.go's runHomelabSubcommand() routes os.Args to the right handler.
All subcommands use internal/config for DB connection and share the SSH
helper from the existing ssh subcommand.
SSH connectivity (for pct, logs, restart) reuses the mesh-address resolution
from internal/domain/ — entity slug → inventory attributes → mesh IP →
ssh user@ip.
Verification
oikos homelabwithout args prints subcommand list (matching Python parity)- Each P0 subcommand works end-to-end from mac-mini
bin/homelab→oikossymlink;homelab list=oikos homelab listbin/oikosremoved
Changelog
- 2026-07-07 — plan created. Cataloged 27 Python subcommands vs 4 Go subcommands.