Commit Graph

55 Commits

Author SHA1 Message Date
Maarten a526c4ea9f Add AWX showcase blueprints: webapp, staticsite, k3scluster
Three end-to-end blueprint examples demonstrating AWX configuring real
applications via Aether lifecycle hooks:

- webapp: nginx+PHP web tier + MariaDB with cross-instance service
  discovery, credential sharing, and OVN ACL management
- staticsite: horizontally scaled nginx with per-instance branding
- k3scluster: K3s control plane + workers with automatic token
  discovery and cluster join (fixes Incus files API symlink issue
  where node-token symlink returns target path instead of content)

Includes dispatcher playbooks, shared helpers (sibling discovery,
temporary internet NIC), decommission handlers, and deploy-awx
changes to push showcase playbooks and create AWX templates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 15:16:49 +01:00
Maarten 5ee32fe08c Update observability guide and refresh all dashboard screenshots
Guide changes:
- Architecture diagram now shows auto-discovered targets and optional
  oc-server monitoring
- Fix /host/rootfs → /host/root mount path in node_exporter section
- Document ARGS variable name (not NODE_EXPORTER_OPTS) for Alpine init
- Add --oc-server and --nodes flag documentation to Management section
- Update resource budget with oc-server row
- Expand "No node metrics" troubleshooting with ARGS diagnostic steps
- Update screenshot captions with correct RAM values (19.5 GiB)

Screenshots refreshed after fixing node-exporter RAM bug — all 9
dashboards now show correct host metrics (20 GiB RAM, not 128 MiB).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 15:14:56 +01:00
Maarten be3f989312 Add cluster auto-discovery, oc-server monitoring, fix node-exporter RAM bug
Three changes to deploy-observability:

1. Fix node-exporter showing 128 MiB RAM: the conf.d template wrote
   NODE_EXPORTER_OPTS but Alpine's init script reads ARGS, so
   node-exporter started with no flags and read container /proc
   instead of host /proc. Changed to ARGS.

2. Auto-discover cluster nodes from `incus cluster list` instead of
   hardcoded arrays. Old values become fallback defaults for --dry-run.
   Added --nodes flag for manual override.

3. Add --oc-server flag to monitor the standalone OC management VM.
   Deploys node-exporter with host mounts and a proxy device to expose
   9100 on the host IP (oc-server uses incusbr0, not OVN). Adds to
   both Incus API and node-exporter Prometheus targets. Updated status
   and cleanup to handle the oc-server container.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 15:00:06 +01:00
Maarten 03cad2dc93 Fix System Info table merge and boot time unit in host-resources dashboard
Replace merge transformation with joinByField on instance — the merge
couldn't combine rows from queries with different label cardinality
(node_uname_info has many extra labels), producing 7 rows instead of 3.

Fix boot time showing "56 years ago" by multiplying node_boot_time_seconds
by 1000 — Grafana's dateTimeFromNow unit expects epoch milliseconds.

Also hide the exported_nodename column (container hostname, not useful).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 14:08:29 +01:00
Maarten 45801dc349 Fix metric source mixing: drop node_* from incus job, relabel node-exporter to friendly names
The incus scrape job (/1.0/metrics) exports both incus_* and node_* metrics,
duplicating the dedicated node-exporter containers. This caused 6 "nodes"
instead of 3 in dashboards, with the incus-sourced entries showing container
artifacts (epoch-0 boot time, 128 MiB RAM).

Prometheus config changes:
- Drop node_* metrics from incus job via metric_relabel_configs
- Relabel node-exporter instances to friendly names (oc-node-01/02/03)
  using per-target nodename labels + relabel_configs

Dashboard changes (belt-and-suspenders):
- Add job="node-exporter" to all template variables and panel queries
  in host-resources, network-deep-dive, storage-filesystem, capacity-planning
- Add Logs Drilldown link to logs-explorer dashboard
- Add Promtail journal relabels for level and service_name

Also: install Logs Drilldown plugin, update observability guide with
Logs Drilldown documentation and screenshots.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 14:05:55 +01:00
Maarten 3520eb76ea Fix Promtail log shipping and Logs Explorer dashboard queries
Promtail was not shipping logs due to missing /var/lib/promtail directory
and insufficient permissions (not in adm or systemd-journal groups).
Logs Explorer dashboard queries failed on Loki 3.x because allValue ".*"
is rejected as an empty matcher — changed to ".+" which requires at least
one character. Removed unit filter from main Logs query since varlog
streams lack the unit label.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 12:43:03 +01:00
Maarten 32d341fc99 Fix dashboard query bugs, add screenshots, update observability guide
Replace incorrect Prometheus queries across 4 dashboard JSON files:
- incus_uptime_seconds → incus_cpu_effective_total for template vars and counts
- incus_memory_RSS_bytes → incus_memory_Active_bytes (RSS only reports for VMs)
- Uptime panel → Processes panel using incus_procs_total
- Remove wrong instance filter from network deep dive instance panels
- Fix Loki data directory permissions in deploy-observability

Add viewport screenshots of all 9 Grafana dashboards and embed them in
the observability guide with captions and updated descriptions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 09:44:03 +01:00
Maarten f1f85d21ea Fix deploy-observability and manage-dashboards issues found during live deployment
deploy-observability:
- Discover DNS from UPLINK config instead of hardcoding 8.8.8.8
- Set ipv4.address on OVN NICs (port security drops unknown IPs)
- Install ca-certificates/curl/gnupg before adding Grafana apt repo
- Fix ACL create syntax (--description flag, not key=value)
- Add NIC recovery after ACL changes (NICs go DOWN)

manage-dashboards:
- Rewrite read_manifest() without PyYAML dependency
- Fix ensure_folder() mixing debug output with return value
- Rewrite install payload generation with heredoc + file I/O
- Separate incus file push from Grafana API call
- Fix while-read loops consuming stdin from incus exec (fd3)

Dashboard JSON:
- Add allValue:".*" to all template variables with includeAll

Screenshots:
- Add Playwright screenshots of all 9 Grafana dashboards

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 08:54:58 +01:00
Maarten 6c7714459b Add professional Grafana dashboard suite with management tooling
- New manage-dashboards script: install/export/list/validate/status
  actions for Grafana dashboard lifecycle via HTTP API, with datasource
  UID portability (${DS_PROMETHEUS}/${DS_LOKI} variables) and folder
  organization
- Reorganize dashboards into core/ and community/ subdirectories with
  dashboard.yaml manifest
- Enhance 3 existing dashboards: template variables (instance/node/
  proxy/server), cross-dashboard navigation links, new panels (instance
  status table, CPU mode breakdown, load averages, memory breakdown,
  filesystem bar gauge, response code distribution, server state
  timeline, connection errors)
- Add 6 new dashboards: Instance Deep Dive, Storage & Filesystem,
  Network Deep Dive, Logs Explorer (Loki), Prometheus Health, Capacity
  Planning (with predict_linear forecasting)
- Update deploy-observability: explicit datasource UIDs in provisioning,
  phase 9 uses manage-dashboards --install, new --workloads action for
  dummy metric generation (workload-web + workload-api containers)
- Update observability guide with all 9 dashboard descriptions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 07:50:13 +01:00
Maarten 99ea7e4123 Add .playwright-mcp/ to gitignore
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 22:54:44 +01:00
Maarten 82f9265164 Add Aether/HAProxy/AWX/observability sections to production lab guide
Replace basic workload sections (old 7-11) with full service stack:
- Section 7: Deploy Aether (golden image, macvlan, post_deploy, cluster connection)
- Section 8: HAProxy HA Load Balancing (deploy-haproxy workflow)
- Section 9: AWX Lifecycle Automation (deploy-awx + Aether integration)
- Section 10: Observability Stack (deploy-observability, Grafana/Prometheus)

Adapt and renumber remaining sections (11-17):
- Network isolation uses HAProxy backends instead of manual workloads
- Live migration uses temporary test VM
- Cleanup includes reverse-order service teardown
- Verification expanded to 19 checks covering all services
- Quick reference adds service endpoints and deploy script tables

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 22:33:59 +01:00
Maarten 6d193158d5 Rewrite production lab guide for OC-managed provisioning
Rewrites sections 0-6 from scratch with OC-managed clustering (tokens,
OC ISOs, auto-registration) replacing manual cluster formation. Updates
sections 7-15 with correct naming (oc-node-* instead of lab-node-*),
fixes device override bug, and adds sequential launch guidance.

Key changes:
- Architecture: new VMID scheme (920/400-402), IP scheme (.120/.140-142)
- Node-01 disk increased to 250 GiB for Aether golden image
- OC provisioning flow: token → older-channel ISO → auto-update
- Fixed root device config: device override instead of device set
- Added image download timing notes (sequential, not concurrent)
- Updated OC Dashboard section for OC-managed deployment
- Updated node replacement procedure with correct VMIDs/IPs
- Updated cleanup to use --cleanup-all --deep --yes

Full lab validated from scratch: deploy, cluster, OVN, workloads,
isolation, peering, load balancing, live migration, evacuation/restore.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 22:19:03 +01:00
Maarten d22ff57cc7 Add language tags to untagged code blocks in aether-help-texts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 19:14:28 +01:00
Maarten 959cc0162c Convert all ASCII CLI output tables to native markdown tables
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 18:53:38 +01:00
Maarten 671d79b77f Convert all ASCII diagrams to mermaid with consistent styling
Replace ASCII art diagrams in 9 guide files with mermaid flowcharts
using a unified style system:

- 5 semantic colors (Okabe-Ito derived, color-blind safe):
  teal (nodes), sky blue (instances), blue (network),
  amber (load balancers), mauve (management)
- classDef for reusable styles, tinted subgraph fills
- Max 2 lines per node, specs in companion tables
- <br/> for line breaks (not \n, which Gitea renders literally)
- Solid arrows for data flow, dashed for control plane

Files converted:
- haproxy-guide.md (1 diagram)
- observability-guide.md (1 diagram)
- api-interception-guide.md (1 diagram)
- incusos-break-fix.md (3 diagrams: partitions, boot chain, updates)
- shared-storage-guide.md (3 diagrams: iSCSI arch, topology, multi-host)
- networking-guide.md (2 diagrams: bridge, OVN topology)
- operations-center-guide.md (1 diagram)
- ovn-deep-dive.md (3 diagrams: lab, bridges, Geneve mesh)
- production-lab-guide.md (1 diagram)
- mermaid-test.md updated as style reference with <br/> fix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 17:31:29 +01:00
Maarten 6293f7ab79 Redesign mermaid test with consistent style guidelines
- Define 5-color semantic palette (Okabe-Ito derived, color-blind safe)
- Use classDef for reusable styles instead of per-node statements
- Limit node text to 2 lines max, move specs to tables
- Consistent node shapes by role (rounded for network, stadium for external)
- Dashed arrows for control plane, solid for data plane
- Tinted subgraph fills matching dominant role color

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 17:17:39 +01:00
Maarten 07c289d75c Add mermaid diagram rendering test
Test file with 6 diagrams converted from ASCII to mermaid format
to verify Gitea renders them correctly before updating all docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 16:56:38 +01:00
Maarten 360fba5680 Execute break-fix exercises, document results and fix VMIDs
Exercises executed on 2026-02-24:
- Exercise 1 (Update Observation): observed download, A/B switch, DB-leader
  transfer during reboot. All 3 nodes updated to 202602240349.
- Exercise 3 (Network Isolation): OVN gateway is SPOF (no failover),
  east-west traffic survives, MAC change causes boot hang.
- Exercise 4 (Full Node Failure): 2/3 quorum held, ~20s detection,
  ~50s recovery, instances auto-restart.
- Exercise 2 (Simulated Failed Update): blocked (no update available).

Fixes: VMIDs corrected from 900-902 to 400-402, added MAC safety rule,
documented Proxmox API --data-urlencode requirement for MAC addresses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 16:51:26 +01:00
Maarten 95316a5bee Add IncusOS break-fix guide, incusos-health helper, immutability rules
Phase 4: Explore IncusOS immutability via the /os/1.0 API:
- incusos-health script with --status/--partitions/--tpm/--services/--network/--update
- Validated: TPM encryption (root+swap), Secure Boot with 4 certs, ZFS pool
- Documented A/B partition scheme, update mechanics, security chain
- Defined 4 break-fix exercises (update observation, failed update, network
  isolation, full node failure) with safety rules and pickup points

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 15:57:43 +01:00
Maarten cc017b648d Add observability stack: Prometheus, Grafana, Loki with dashboards
Deploy and validate full monitoring stack on the Incus cluster:
- Prometheus scraping 9 targets (3 Incus nodes, 2 HAProxy, 3 node-exporters, self)
- Grafana with 3 dashboards (cluster overview, HAProxy traffic, host resources)
- Loki + Promtail for log aggregation
- OVN network forward for LAN access (192.168.103.201:3000/9090)
- deploy-observability script with --deploy/--status/--cleanup/--doctor
- Solved OVN ACL challenges: Aether default-deny requires monitoring-allow ACL

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 15:44:59 +01:00
Maarten 91a87456a5 Add API interception guide, incus-mitm helper, and payload rules
Phase 2: captured Aether↔Incus API traffic using the event stream.
Identified callers by TLS certificate fingerprint (CLI vs Aether vs
cluster nodes). Documented API patterns for stop/start/snapshot/exec.
Key finding: Aether subscribes to lifecycle events and reactively
queries affected resources.

Deliverables:
- notes/api-interception-guide.md: capture setup + all API patterns
- incusos/helpers/incus-mitm: --capture/--live/--analyze/--callers
- .claude/rules/aether-api-payloads.md: context for AI assistants
- .mcp.json: add --ignore-https-errors for Playwright

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 15:01:56 +01:00
Maarten 94eb3c7cc4 Add OVN deep-dive guide, ovn-inspect helper, and internals rules
Phase 1 of observability plan: mapped the complete OVN topology at
logical (NB), physical (SB), and OVS layers. Traced packet path from
LAN through VIP LB to HAProxy backends. Documented naming conventions,
gateway failover, Geneve tunnels, and MTU implications.

Deliverables:
- notes/ovn-deep-dive.md: comprehensive OVN internals guide
- incusos/helpers/ovn-inspect: --nb/--sb/--ovs/--full/--trace actions
- .claude/rules/ovn-internals.md: context for AI assistants

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 14:52:37 +01:00
Maarten c047bc6ed4 Add HAProxy LB guide, deploy script, Playwright helper; validate end-to-end
Tear down and fully re-deploy HAProxy infrastructure from scratch to
validate the guide reads correctly. Key fixes from re-validation:

- Fix session API field names: lb_vip (not vip), target_ip/target_port
  (not ip/port), health_check_enabled (not health_check)
- Document that Aether does NOT create the OVN load balancer — must be
  created manually via incus network load-balancer
- Fix internal VIP test: OVN LB doesn't hairpin, use localhost instead
- Document OVN LB has no health checking (HAProxy failover not instant)
- Add cleanup step for manually-created OVN LB
- Add aether-browser Playwright helper and .mcp.json config
- Add deploy-haproxy script, haproxy-lb rules, full guide

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 14:08:43 +01:00
Maarten 0100182dd8 Validate AWX guide, document Aether API, enforce API-first policy
- AWX guide: fix wrong project ID (6→9), add dynamic ID lookups,
  add missing Step 5 push commands, add migration pod to expected
  output, replace PostgreSQL workaround with Aether UI instructions,
  reference deploy-awx as automated path
- Aether guide: document JWT REST API (auth, clusters, ACLs),
  swagger UI at /api/docs, rate limits, coverage vs UI-only features
- CLAUDE.md: add Aether API capability, add interaction hierarchy
  (API first → UI fallback → never root/DB in guides)
- deploy-awx: fix PLAYBOOK_DIR from "ansible/playbooks" to "playbooks"
- post-deploy.yml: clarify cert push path vs EE runtime mount path

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 10:33:46 +01:00
Maarten c4efcbe30d Modularize CLAUDE.md: rules, helpers, skills (866→112 lines)
Split monolithic CLAUDE.md into three layers:
- CLAUDE.md (112 lines): behavioral focus, capabilities, safety rules
- .claude/rules/ (8 files, 503 lines): topic-specific context with
  paths: frontmatter for conditional loading
- .claude/skills/ (2 files): /screenshot and /proxmox-api commands

Add helper scripts (incusos/helpers/):
- proxmox-screenshot: SSH→screendump→PPM→PNG pipeline with VMID safety
- proxmox-api: authenticated API calls handling ! in token quoting

Fix git workflow (master branch, both origin+aether remotes).
Add capabilities section for screenshots, API calls, live AWX output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 09:48:25 +01:00
Maarten 0b82b9ce3b Validate AWX guide against real infrastructure, fix all bugs
End-to-end tested: Aether deploy triggers AWX post-deploy job,
container gets configured (hostname, packages, timezone, deploy-info),
decommission job runs on delete. All playbooks validated working.

Key fixes from testing:
- Playbooks: use Incus REST API (not SSH) — bridge subnet not routable
  from management VLAN, nftables blocks inbound forwarding
- Aether vars: ffsdn_* prefix (not vm_name/vm_ip/environment/owner)
- AWX EE paths: /runner/project/ (not /var/lib/awx/projects/)
- AWX access: NodePort 30080 (not Traefik ingress — 404 for IP access)
- deploy-awx: systemd-networkd (not netplan), manual project (not git
  SCM — EE can't reach private repo), Incus cert push, prereq install
- Remove unused base-config role (tasks inlined as setup script)
- Simplify ansible.cfg (no SSH settings needed)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 01:53:54 +01:00
Maarten 6a74925fc4 WIP: Add AWX deployment, Ansible playbooks, and Aether integration
Work-in-progress commit to enable AWX project sync from git.
Files will be updated with validated output from real deployment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 00:04:28 +01:00
Maarten fd30ef27d4 Expand Aether guide: deploy forms, blueprints, instance mgmt, OVN recovery
Document the full Aether platform after hands-on testing:
- Deploy form fields, validation, IP reservation, AWX integration
- Blueprint design (cluster-agnostic, component structure, JSON API)
- Blueprint deployment lifecycle (3-phase deploy, decommission workflow)
- Instance management (state control, resources, snapshots, console, ACLs)
- OVN recovery procedure after ovn-central container move (5-phase)
- Node resize procedure (Proxmox disk grow + ZFS pool expansion)
- Update lab-oc-nodes defaults to match resized nodes (20G RAM, 100G disk)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 22:59:30 +01:00
Maarten d2b49f5ef0 Add Aether management platform guide and extracted help texts
Aether deployed on oc-node-01, connected to Incus cluster (trust token)
and Operations Center (mutual TLS). Extracted all 16 built-in help pages
(426 KB) covering cluster management, storage, profiles, instances,
HAProxy LB, firewall ACLs, blueprints, and health monitoring.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:24:01 +01:00
Maarten ccd8e87be6 Sync READMEs: add missing guides and example configs
README.md: add networking, shared-storage, production-lab guides;
reorder notes in learning sequence.
CLAUDE.md: add production-lab, version-compat, iso-download to structure.
incusos/README.md: add 5 missing example configs, organize by category.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 15:11:35 +01:00
Maarten 2f04a4838c Trim CLAUDE.md: condense crontab bug section to essentials
Remove investigation history, experiment data, upstream fix proposals,
and source code analysis. Keep only: what the bug is, our fix (omit
force_reboot), detection/auto-heal, and TPM corruption warning.

959 → 792 lines (-17%).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 15:02:11 +01:00
Maarten 4854f15a16 Add shared storage guide: iSCSI + lvmcluster with tested output
Validated end-to-end on OC-managed 3-node cluster: iSCSI target
container (tgt), IncusOS iSCSI/LVM services, lvmcluster pool,
container migration (0.12s), VM live migration (~6s, RAM only).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 14:58:11 +01:00
Maarten 896731a7ed Validate OC guide: teardown + rebuild with fresh output
Full teardown and rebuild of the OC-managed cluster lab to validate
the guide works end-to-end with zero manual interventions. Key fixes:

- Add missing `provisioning channel add old-stable` step (was causing
  "Not found" error on `assign-channels`)
- Fix command paths: `examples/` → `incusos/examples/` (repo-relative)
- Fix version numbers: Incus 6.12 → 6.21, OC output format
- Fix update list table format (Severity not Sev, add Published At note)
- Fix cluster list: remove premature ovn-chassis roles at Section 4.4
- Add WARNING line to `admin os show` output
- Add TLS cert acceptance note for `remote add`
- Add cleanup --yes flag to teardown commands
- Add test container cleanup command in Section 5.7
- Note variable-per-deployment values (incusbr0 subnet, token UUIDs)
- Add `provisioning channel add` to Quick Reference and CLI table
- Add channel creation to troubleshooting table
- Use consistent <token-UUID> placeholder throughout

All 16 verification checklist items PASS. Lab left running.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 12:23:49 +01:00
Maarten b9f1894681 OC-managed cluster with OVN and HA nginx: full validated deployment
Deploy a 3-node OC-managed Incus cluster with OVN overlay networking
and an HA nginx workload behind an OVN load balancer. Key discoveries:

- OC needs_update blocker: OC tracks update delivery through its pipeline,
  not version comparison. Nodes deployed from the latest ISO are stuck at
  needs_update=true. Fix: generate ISO from older channel (--channel
  old-stable), let OC push the real update after registration.
- ISO upload skip bug: incusos-proxmox silently reused stale ISOs with the
  same filename. Fixed: --iso now replaces existing same-named ISOs.
- Empty app-seed-config ({}): avoids "Certificate already in trust store"
  Terraform error when cert is already injected via SEED_DATA.

Files changed:
- notes/operations-center-guide.md: full rewrite with real captured output
- CLAUDE.md: needs_update findings, ISO fix, OVN LB behavior
- incusos/examples/lab-oc-deploy.yaml: static IP for VLAN 69
- incusos/examples/lab-oc-nodes.yaml: static IPs + disk sizing
- incusos/incusos-proxmox: fix stale ISO reuse when --iso provided

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:08:28 +01:00
Maarten d8f2a067c4 Add production lab guide with validated output from real deployment
Comprehensive 1775-line guide (notes/production-lab-guide.md) covering
full lab lifecycle: OC dashboard, 3-node manual clustering, OVN overlay
networking, mixed workloads, live migration, network security, load
balancers, and cluster evacuation/restore. Every command was executed
on real infrastructure (2026-02-22) and output captured.

Key findings incorporated:
- IncusOS NIC is named `mgmt`, not `ens18` (UPLINK parent)
- OC CLI requires `remote switch`, not `remote:` suffix syntax
- LB/forward backends need IP addresses, not instance names
- Cluster auto-schedules without --target flag
- Migration ~7s at ~140 MB/s with heartbeat continuity

Also: lab-production.yaml config, lab-oc.yaml apply_defaults fix,
CLAUDE.md and networking-guide.md updates for mgmt/OC/LB findings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 23:46:10 +01:00
Maarten 088baf6807 Fix IncusOS deploy: eliminate crontab bug and TPM corruption
Root cause #1: force_reboot triggers SysRq-B intermediate boot that
races with IncusOS state initialization, causing ~50% crontab bug rate.
Fix: omit force_reboot from seed — VM sits at "please remove media"
after install, blockstat detects idle cleanly, no intermediate boot.
Validated: 15/15 PASS (100%) on 4-core VMs.

Root cause #2: 90s boot timeout was too short for first-boot sysext
download under load. Premature retry hard-stops the VM during first
boot, corrupting the TPM encryption key permanently. Fix: increase
boot timeout to 180s (60s sleep + 120s polling).

Changes:
- incusos-proxmox: remove --force-reboot from seed generation, increase
  boot timeout from 90s to 180s, update retry comments
- incusos-seed: add --ip/--gateway/--dns flags for static IP seeds,
  add network.yaml generation
- observe-deploy: add --no-force-reboot flag, --runs N mode, VLAN
  support, scrub_schedule health check, 1s screenshot interval
- lab configs: add static IPs for VLAN 69 (192.168.102.x/22)
- proxmox.yaml.example: add vlan, gateway, dns fields
- CLAUDE.md: document findings, boot timeout, TPM corruption risk

Tested: single VM, 3-node cluster, OC + 3 nodes — all 100% success.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 21:50:02 +01:00
Maarten 9e3201393e Add VLAN 69 support, OVN networking guide, and lab-networking config
VLAN tagging: incusos-proxmox now reads optional `vlan` key from
proxmox.yaml and lab configs, appending `tag=N` to the VM NIC spec
in both SSH and API creation paths. Tested manually on VMID 850 —
VM received IP in 192.168.100.0/22 (Homelab VLAN). VLAN tag is
Proxmox-level only; IncusOS and Incus instances are unaware of it.

Networking guide: comprehensive tutorial covering bridge networking,
OVN overlay setup on IncusOS (services disabled by default, must
enable via REST API), cross-node connectivity, isolation, ACLs,
peering, load balancers, forwards, and DNS. All scenarios tested
on a 4-node OC-managed cluster.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 13:28:03 +01:00
Maarten 2e2447de06 Boot failure investigation: 67% crontab bug rate confirmed, version mismatch ruled out
Parallel investigation of IncusOS issue #843 across 20 VM deploys confirms
the "invalid crontab expression" error hits 67% of first boots. The bug is
a race condition in the daemon startup — NOT related to ISO/CDN version
mismatch, update downloads, or seed config. Sysext downloads occur on every
first boot even with matching versions.

New tools: investigate-parallel (parallel VM testing), pve-api (Python
Proxmox API helper avoiding bash ! quoting), investigate-boot (sequential
testing), test-boot-reliability (batch reliability testing).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 23:19:35 +01:00
Maarten 52d8dbdc89 OC v0.3.0 comprehensive test: retry logic, auto-heal, apply_defaults validation
Phase 0-10 of OC test plan completed:
- Add --retries N flag to incusos-proxmox (default 3, configurable boot retries)
- Add fix_scrub_schedule() auto-heal for IncusOS crontab bug via REST API
- Validate apply_defaults: false for OC-managed nodes (recommended approach)
- Test full OC lifecycle: deploy, provision, cluster, workloads, migration,
  evacuation, node failure/recovery, inventory query, REST API
- Update CLAUDE.md: OC v0.3.0 commands, --retries, scrub fix, node recovery
- Update operations-center-guide.md: v0.3.0, apply_defaults, scrub fix,
  node failure simulation, IncusOS REST API docs, inventory query

Key findings:
- apply_defaults: false lets OC Terraform handle everything cleanly
- fix_scrub_schedule() proactively heals ~50% crontab boot failure
- OC does not detect node failures (always shows "ready")
- inventory query (v0.3.0) provides cross-resource tree views
- Node crash recovery: auto-rejoin in ~60s, containers auto-start

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 21:41:34 +01:00
Maarten b8e7a7ca62 Update boot failure analysis: error is persistent within VM session
Verified across 6 observe-deploy runs (3/6 hit crontab error, all 6 PASS).
Key finding: the error persists across systemd daemon restarts because the
deferred Save() writes corrupt state.txt (encoder skips zero-value
ScrubSchedule). Recovery requires Proxmox stop+start (hard power-off may
lose uncommitted corrupt write). Updated CLAUDE.md accordingly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 18:16:34 +01:00
Maarten 1499dd7072 Add observe-deploy diagnostics, boot failure analysis, and deploy reliability fixes
Observational single-VM deploy tool (observe-deploy) captures frame-by-frame
console screenshots via Proxmox SSH root screendump to diagnose IncusOS boot
failures. Four deploys revealed ~50% "invalid crontab expression" hit rate
with no correlation to update.yaml presence.

Deep analysis of IncusOS source code (main.go, state/, scheduling/, install.go)
traced the full crash path: registerJobs() validates ScrubSchedule via gocron
at step 19 of startup(), after applications start at step 16 and REST API
starts even earlier. Most likely a race condition or gocron edge case.

Key fixes:
- incusos-proxmox: automatic retry on first-boot failure (stop+start cycle)
- incusos-proxmox: pve_destroy_vm now waits for async UPID task completion
- observe-deploy: same UPID wait for destroy, full screenshot pipeline
- incusos-seed: always generate update.yaml with explicit check_frequency
- CLAUDE.md: complete boot sequence timeline, source-level root cause analysis,
  proposed upstream fix for IncusOS registerJobs() defensive validation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 13:42:51 +01:00
Maarten 00f57746ff Auto-load env file for PROXMOX_TOKEN_SECRET on startup
Scripts now search for an 'env' file up the directory tree from the
script location and source it automatically. No manual 'source env'
needed. Falls back gracefully -- if no env file exists and the secret
isn't exported, require_api_auth() catches it with a clear error.

Also fix unbound variable in --labs when pool has no VMs (declare -A
needs explicit =() initialization under set -u).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 08:56:28 +01:00
Maarten e0faba8980 Fix critical auth validation, role naming, Sys.Audit, and apply_defaults
Safety fix: --cleanup-all, --resources, --labs silently failed when
PROXMOX_TOKEN_SECRET was not set, reporting "No VMs found" instead of
an auth error. Added require_api_auth() gate and error propagation in
pve_list_vms() to fail loudly on API auth failures.

Also:
- Standardize role name to IncusOSDeployer across all docs and scripts
- Add Sys.Audit privilege to all setup command examples
- Document /nodes/pve ACL requirement for scoped permissions
- Update lab-cluster.yaml: apply_defaults: false for joining nodes
  (upstream IncusOS recommendation to avoid 8-command cleanup)
- Add full cluster automation to lab-test phase_cluster:
  core.https_address setup, remote cert fixup, automated join prompts,
  conditional defaults cleanup for apply_defaults: true nodes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 08:51:52 +01:00
Maarten ca660585dc Correct reboot diagnosis: OC-specific, not dqlite/TPM corruption
Volume testing with live Proxmox infrastructure proved that guest reboot
does NOT break standalone IncusOS nodes. Tested:
- 4 consecutive Proxmox stop/start cycles: all clean, data intact
- Single-node guest reboot: recovered in ~50s, containers auto-started
- Simultaneous 3-node guest reboot: all recovered, data integrity verified

The "reboot breaks IncusOS" issue is OC-specific. Console errors showed:
- Node 1: "failed to parse update frequency" (OC pushed bad crontab)
- Node 2: stuck at "starting application" (waiting for peers in loops)
- Node 3: "constraint violation" (OC re-registering existing server)

Updated all 8 references across CLAUDE.md, operations-center-guide.md,
and TESTING.md to reflect the corrected diagnosis.

Also fixed one more bare return bug in do_list_labs (empty pool path).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 00:48:06 +01:00
Maarten 37d63c35a6 Fix exit code bugs, doctor output, and labs parsing from live testing
Tested all new commands (--resources, --labs, --lab-down, --lab-up,
--doctor, --status) against live Proxmox with 4 running IncusOS VMs.

Fixes:
- return → return 0 in 4 early-exit paths (pipefail + [[ ]] false
  propagated non-zero exit codes)
- do_list_labs: broken ERE regex [^\ \]]+ → [^\ ]+ for config tag
- do_list_labs: line-based Python output instead of delimiter-based
  tr (descriptions with spaces broke read parsing)
- do_list_labs: unsafe echo|tr|sort pipeline → printf|sort
- run_doctor: operations-center version → --version (shows 0.2.2)
- run_doctor: ls glob pipeline crash under pipefail (|| true)

CLAUDE.md:
- Document env file (PROXMOX_TOKEN_SECRET) and source workflow
- Add Sys.Audit to minimum API privileges (needed for --resources)
- Correct role name to IncusOSDeployer
- Add /nodes/pve ACL requirement for node-level endpoints

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 00:08:29 +01:00
Maarten f812e04746 Add macOS support, multi-lab lifecycle, extended testing, and migration docs
Fix OC reboot diagnosis across all docs (dqlite corruption, not TPM PCR).
Add cross-platform support: genisoimage/mkisofs detection, portable dd/grep/
date usage. Add --lab-up/--lab-down/--resources/--labs to incusos-proxmox for
multi-lab coexistence. Add 7 new lab-test phases (storage, network, projects,
backup, limits, security, resilience). Fix VMID collision in lab-advanced
(910→930). Create migration guide and UTM design doc.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 23:41:06 +01:00
Maarten 8ae8700346 Add Operations Center hands-on testing, hybrid deployment, and comprehensive guide
Deployed and tested the full OC provisioning pipeline on Proxmox:
- Hybrid approach: incusos-proxmox --iso with OC-provisioned ISOs
- Token seeds, auto-registration, single-command cluster formation
- Inventory sync, live migration visibility, evacuation, node replacement
- Discovered OC reboot breaks TPM encryption on Proxmox VMs

Key findings documented: dual-seed coexistence, apply_defaults conflict
with Terraform, inventory requires explicit resync, stale server entries
from out-of-band changes, meshbr0 network added by OC clustering.

New files:
- notes/operations-center-guide.md: full tested OC reference
- incusos/examples/lab-oc-deploy.yaml: OC server deployment config
- incusos/examples/lab-oc-nodes.yaml: 3-node OC cluster config

Updated: CLAUDE.md (OC section rewrite), TESTING.md (hybrid workflow),
.gitignore (env file with Proxmox secrets)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 22:55:48 +01:00
Maarten 732d70b38e Add advanced migration testing, cluster lifecycle, and clustering guide
- New lab-advanced.yaml (3-node 6/4/4 core heterogeneous cluster) and
  lab-replace.yaml (single replacement node) for advanced migration and
  cluster lifecycle testing
- New notes/clustering-guide.md with full clustering walkthrough: cluster
  formation, storage pool conflict fix, join workflow, VM live migration
  with limits.cpu range fix, multi-vCPU test results (2/3/4 vCPUs),
  edge cases (I/O, memory, concurrent), and node replacement lifecycle
- Updated CLAUDE.md with clustering docs, rebalancing config keys,
  cluster enable syntax fix (TWO args not one), cluster remove syntax,
  agent reconnect timing (3-4s not 1-2s), and lifecycle procedure
- proxmox.yaml.example for Proxmox connection config reference

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 18:46:55 +01:00
Maarten 2aca6fb33b Add lab validation tooling: doctor check, deep cleanup, lab-test script
Implements the first wave of lab validation infrastructure:

incusos-proxmox enhancements:
- --doctor flag: standalone environment health check (tool versions,
  IncusOS CDN availability, Proxmox connectivity). Works with or
  without a config file.
- --cleanup --deep: extended cleanup that also removes seed ISOs,
  IncusOS ISOs from Proxmox storage, matching incus remotes, and
  the local ISO cache (~/.cache/incusos-proxmox/iso-cache/).
- --cleanup-all: pool-wide cleanup that destroys ALL VMs with the
  [incusos-lab:managed] marker in the configured resource pool.
  Requires a pool to be set in config for safety scoping.

New script -- lab-test:
- Guided lab validation with 5 phases: deploy & verify, single-node
  workloads (container + VM lifecycle), cluster formation (enable +
  join via incus remotes), cluster workloads (scheduler-placed +
  targeted), and migration & evacuation (stop/move + evacuate/restore).
- Interactive by default with --yes for unattended runs.
- Reports PASS/FAIL/SKIP per test with a summary.
- Supports --dry-run, --skip-deploy, --cleanup, per-phase execution.
- Reads the same YAML config format as incusos-proxmox.

New example configs:
- lab-cluster.yaml: 3-node Incus cluster (VMID 900+) for testing
  clustering, workloads, and migration.
- lab-oc.yaml: Operations Center + 3 Incus nodes (VMID 910+) for
  testing OC-managed provisioning and cluster formation.

Documentation:
- CLAUDE.md: added clustering workflow (incus cluster enable/add/join
  via remotes), lab-test overview, Operations Center CLI notes,
  doctor and cleanup feature descriptions.
- README.md: added lab-test section with usage/phases/options, doctor
  check docs, deep cleanup docs, new example files in listing.
- TESTING.md: added full lab validation guide (quick start, phase-by-
  phase testing, manual cluster formation alternative, OC lab workflow
  with CLI setup and provisioning steps), cleanup instructions.

Not yet tested against live infrastructure -- next step is to deploy
lab-cluster.yaml, add incus remotes, and run lab-test end-to-end.
Known areas to validate: cluster join syntax with remotes, VM nested
virt support, evacuation behavior, apply_defaults consistency across
cluster members.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 22:00:05 +01:00
Maarten a0a4b48c73 Implement end-to-end IncusOS Proxmox deployment via API
Major changes to incusos-proxmox and incusos-seed to achieve fully automated
IncusOS deployment to Proxmox VE. Fixes multiple blockers discovered during
testing: FAT→ISO 9660 seed format (CD-ROM label detection), preseed YAML
structure (Go yaml:",inline"), disk target auto-detection, install completion
monitoring via blockstat disk I/O, install media ejection, and ARP-based IP
detection for the agent-less IncusOS.

Key changes:
- incusos-seed: add --format iso using genisoimage, fix preseed structure
- incusos-proxmox: ISO seeds, blockstat install detection, media ejection,
  ARP-based IP lookup, force_reboot requirement
- Documentation: comprehensive updates to CLAUDE.md, README.md, TESTING.md
- Examples: fix preseed.certificates path in all YAML examples
- Notes: full session progress with all bugs found and fixed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 15:26:58 +01:00