# Implementation Plan: Safe Ansible MVP for Nimrod-Managed Homelab Updates

## Phase 1: Document scope, classes, and safety gates

### Changes

#### 1. Update-management runbook
**File:** `runbooks/homelab-managed-updates.md`  
**Action:** create

Include:
- Execution location: Nimrod LXC / repo checkout at `/home/piagent/projects/nimrod`.
- No unattended/automatic critical updates in MVP.
- Required preflight gates: clean git tree, SSH alias reachability, host class confirmation, backup/snapshot status noted, service health check noted.
- Update classes:
  - `experimental`: low-risk disposable/non-core services; eligible for first trials.
  - `standard`: routine guest OS package updates for non-secret, non-network-core services with manual operator start.
  - `critical_manual`: reverse proxy, DNS/Unbound, Vaultwarden/secrets, Nextcloud, Nimrod workspace; inspect changelog and approve explicitly before package upgrade or reboot.
  - `manual_only`: Proxmox host/hypervisor and any data-destructive or major-version service upgrades.
- MVP rule: Ansible may check/update only explicitly targeted hosts; no broad `all` upgrade runs.
- Change-log requirement: any server-side mutation must be logged in `docs/server-change-log.md`.

#### 2. Ticket notes
**File:** `tickets/active/2026-06-06-managed-update-schedules-ansible.md`  
**Action:** modify

Add link to this plan and the new runbook once created. Do not mark trial complete until an implementer performs and verifies the first non-critical update.

### Verification
Automated:
- [ ] `test -f runbooks/homelab-managed-updates.md`
- [ ] `grep -n "No unattended/automatic critical updates" runbooks/homelab-managed-updates.md`
Manual:
- [ ] Confirm the runbook names `homepage-dashboard` as first trial target and keeps reverse-proxy, unbound, vaultwarden, nimrod, and nextcloud behind manual approval.

### Rollback
- Delete `runbooks/homelab-managed-updates.md` and remove its ticket link if the approach is rejected.

## Phase 2: Add minimal Ansible project files

### Changes

#### 1. Ansible config
**File:** `ansible/ansible.cfg`  
**Action:** create

Recommended settings:
- `inventory = inventories/homelab/hosts.yml`
- `host_key_checking = True`
- `retry_files_enabled = False`
- `stdout_callback = yaml`
- Avoid embedding secrets or private key paths unless already repo-approved and non-secret; prefer existing SSH config/agent behavior.

#### 2. Homelab inventory
**File:** `ansible/inventories/homelab/hosts.yml`  
**Action:** create

Use current `.pi/ssh/hosts.json` aliases and documented guests:
- `homepage-dashboard` / `192.168.0.241` / class `experimental` / first trial target.
- `reverse-proxy` / `192.168.0.137` / class `critical_manual`.
- `unbound-dns` / `192.168.0.124` / class `critical_manual`.
- `vaultwarden-vm` / `192.168.0.238` / class `critical_manual`.
- `nimrod-lxc` / `192.168.0.222` / class `critical_manual`.
- `nextcloud-vm` / `192.168.0.110` / class `critical_manual`.

Groups to include:
- `homelab_guests`
- `linux_lxc`
- `linux_vm`
- `update_experimental`
- `update_standard` initially empty
- `update_critical_manual`
- `first_trial`

Use `ansible_user: piagent`. Set `ansible_host` to IPs from `.pi/ssh/hosts.json`/`infra/proxmox-registry.yaml`; add comments that live state must be verified before mutation.

#### 3. Requirements placeholder
**File:** `ansible/requirements.yml`  
**Action:** create

Keep minimal; include only needed built-in/community collections if implementation discovers they are absent. Prefer core `ansible.builtin.apt` for Debian/Ubuntu package updates.

### Verification
Automated:
- [ ] `ansible-inventory -i ansible/inventories/homelab/hosts.yml --list`
- [ ] `ansible-inventory -i ansible/inventories/homelab/hosts.yml --graph`
Manual:
- [ ] Confirm inventory hostnames match `.pi/ssh/hosts.json` aliases where available.
- [ ] Confirm no Proxmox host/hypervisor target is included in MVP inventory.

### Rollback
- Remove the `ansible/` directory if MVP Ansible layout is rejected.

## Phase 3: Add read-only preflight playbooks

### Changes

#### 1. Ping/connectivity preflight
**File:** `ansible/playbooks/preflight.yml`  
**Action:** create

Include read-only tasks only:
- `ping`
- gather facts
- print distribution/version/kernel/update class
- check pending reboot marker paths read-only, e.g. `/var/run/reboot-required`

#### 2. Package check playbook
**File:** `ansible/playbooks/check-updates.yml`  
**Action:** create

Include non-mutating package status checks:
- refresh package cache only if considered acceptable in implementation; otherwise use shell checks such as `apt list --upgradable` with `changed_when: false`.
- summarize upgrade count.
- do not run `apt upgrade`.

### Verification
Automated:
- [ ] `ansible-playbook -i ansible/inventories/homelab/hosts.yml ansible/playbooks/preflight.yml --limit first_trial --check`
- [ ] `ansible-playbook -i ansible/inventories/homelab/hosts.yml ansible/playbooks/check-updates.yml --limit first_trial --check`
Manual:
- [ ] Confirm play recap reports no changes for read-only checks.
- [ ] Confirm only `homepage-dashboard` is targeted during first validation.

### Rollback
- Delete `ansible/playbooks/preflight.yml` and `ansible/playbooks/check-updates.yml` if the checks are noisy or unsafe.

## Phase 4: Add manually-triggered update playbook for MVP

### Changes

#### 1. Safe apt update playbook
**File:** `ansible/playbooks/apt-upgrade.yml`  
**Action:** create

Safety requirements:
- Refuse to run unless `target_update_class` is passed and matches host `update_class`.
- Refuse `critical_manual` unless `allow_critical_manual=true` is explicitly passed.
- Default limit documented as `--limit first_trial`; never instruct broad `all` use for upgrades.
- Use Debian/Ubuntu apt tasks only.
- Perform package cache update and safe package upgrade, not distro release upgrade.
- Detect reboot requirement but do not reboot in MVP unless a separate explicit variable such as `allow_reboot=true` is passed; default no reboot.
- Emit a summary suitable for `docs/server-change-log.md`.

Recommended first mutation command after preflight succeeds:
- [ ] `ansible-playbook -i ansible/inventories/homelab/hosts.yml ansible/playbooks/apt-upgrade.yml --limit first_trial -e target_update_class=experimental`

### Verification
Automated:
- [ ] `ansible-playbook -i ansible/inventories/homelab/hosts.yml ansible/playbooks/apt-upgrade.yml --limit first_trial -e target_update_class=experimental --check --diff`
- [ ] After real trial only: rerun `ansible-playbook -i ansible/inventories/homelab/hosts.yml ansible/playbooks/check-updates.yml --limit first_trial --check`
Manual:
- [ ] Before real trial, confirm `homepage-dashboard` has snapshot `post-homepage-dashboard-mvp` or another current rollback point.
- [ ] After real trial, verify Homepage remains reachable at `dashboard.dropcutstud.io` or `http://192.168.0.241:3000`.
- [ ] Record host, reason, commands, changed packages, verification, reboot status, and rollback notes in `docs/server-change-log.md`.

### Rollback
- If package update breaks Homepage, use service-level rollback first: restart failed services/containers using `runbooks/homepage-dashboard.md`.
- If service-level rollback fails and snapshot was verified, restore the Proxmox guest snapshot for CTID 107 with explicit operator approval.
- Revert repo changes with `git restore ansible runbooks/homelab-managed-updates.md tickets/active/2026-06-06-managed-update-schedules-ansible.md` before commit, or `git revert <commit>` after commit.

## Phase 5: Review and commit

### Changes

#### 1. Git review
**File:** repo-wide  
**Action:** modify only through review/commit metadata

Run:
- `git status --short`
- `git diff -- ansible runbooks/homelab-managed-updates.md tickets/active/2026-06-06-managed-update-schedules-ansible.md`

Commit as one logical MVP docs/config/playbook change only after check-mode verification passes.

### Verification
Automated:
- [ ] `git status --short`
- [ ] `ansible-inventory -i ansible/inventories/homelab/hosts.yml --list`
Manual:
- [ ] Confirm no secrets, tokens, private keys, or live destructive commands were committed.
- [ ] Confirm no automatic schedule/timer/cron was added for critical hosts.

### Rollback
- Before commit: `git restore ansible runbooks/homelab-managed-updates.md tickets/active/2026-06-06-managed-update-schedules-ansible.md`.
- After commit: `git revert <commit>`.

## Recommended repo paths/files

- `ansible/ansible.cfg`
- `ansible/inventories/homelab/hosts.yml`
- `ansible/playbooks/preflight.yml`
- `ansible/playbooks/check-updates.yml`
- `ansible/playbooks/apt-upgrade.yml`
- `ansible/requirements.yml`
- `runbooks/homelab-managed-updates.md`
- `docs/server-change-log.md` for operational trial logging
- `tickets/active/2026-06-06-managed-update-schedules-ansible.md` for ticket progress

## First non-critical trial target

Use `homepage-dashboard` / CTID 107 / `192.168.0.241` as the first non-critical Ansible trial target. It is lower risk than DNS, reverse proxy, Vaultwarden, Nimrod, or Nextcloud, has a documented runbook, and has a documented snapshot `post-homepage-dashboard-mvp`.

## Unresolved questions / gates before real mutation

- Confirm Ansible is installed/available on Nimrod LXC, or install it as an implementation prerequisite.
- Confirm passwordless or approved `become` behavior for `piagent` on the trial target.
- Verify live IPs and snapshot state immediately before any real update.
- Define maintenance windows before adding any schedule; MVP should remain manual-only.
- Decide whether future inventory should be generated from `infra/proxmox-registry.yaml`; MVP may use checked-in static inventory derived from current docs.
