Add zfs_pool_mode: file|single|mirror.
- file (default): loopback disk image at zfs_pool_file_path — real ZFS with
no spare disk, ideal for a cost-optimized test VM; wipes nothing.
- single/mirror: whole spare disk(s); mirror for production redundancy.
Guard/probe now loops over zfs_pool_disks. Update group_vars, README (modes,
prerequisites, safety), and CLAUDE.md current-focus note.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Some sanoid packages (e.g. on Ubuntu 26.04) don't ship /etc/sanoid, so the
config template failed with 'Destination directory does not exist'. Create
the directory explicitly, and copy the packaged sanoid.defaults.conf into
it (sanoid requires it beside sanoid.conf) so the first timer run succeeds.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
'No package matching docker-ce' happens when Docker has no repo for the
VM's release codename (common on non-LTS Ubuntu). Add docker_apt_codename
(defaults to the detected release; override to e.g. noble on non-LTS) and
refresh the apt cache right after adding the repo so index errors surface
immediately instead of as a missing package.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The six.moves ModuleNotFoundError comes from version-skewed duplicate
Ansible installs; recommend a single pipx install in prerequisites.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The playbook runs everything via become(root); note that -K
(--ask-become-pass) is required unless the target user has passwordless
sudo. Replace the misleading --check first-run hint with --syntax-check.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Running the playbook from /mnt/c in WSL makes Ansible ignore ansible.cfg
(world-writable dir), losing the inventory. Document the fix (copy to WSL
home, or export ANSIBLE_CONFIG) plus the inventory/syntax-check reminders.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements the two decoupled backup streams from docs/06 as an idempotent
Ansible role wired into the host playbook:
- Files: sanoid takes/prunes ZFS snapshots per policy (sanoid_datasets) on
its packaged timer; syncoid replicates offsite (heleos-zfs-offsite),
enabled only when zfs_offsite_target is set.
- DB: heleos-db-backup (nightly systemd timer) walks the deployments dir and
dumps each DB-backed site via `docker exec mariadb-dump` into
db-backups/<customer>/<site>/{daily,weekly,monthly} with rotation
(automysqlbackup-style, adapted for the containerized DB; MYSQL_PWD keeps
the password out of the process list). heleos-db-offsite rsyncs offsite
when db_offsite_target is set.
Streams and schedules are configured in group_vars/all.yml; offsite is
opt-in via the two target vars. Updates doc 06 (implementation note), the
ansible README, and CLAUDE.md status. YAML + templates validated by render.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add base Docker Compose projects under platform-infra/stacks, using the
Phase 1 ZFS platform datasets and shared networks:
- bootstrap-networks.sh: idempotent creation of shared `proxy` (edge) and
`platform` (internal) Docker networks.
- traefik: edge router with TLS/ACME (HTTP-01), global HTTP->HTTPS redirect,
file-based security-headers/TLS middlewares, and a basic-auth dashboard.
Docker access via a least-privilege tecnativa/docker-socket-proxy instead
of mounting docker.sock directly.
- mariadb: shared instance on tank/platform/mariadb, utf8mb4, tuned; not
published to the host (internal `platform` network only).
- forgejo: Git + container registry over HTTPS (SSH disabled to avoid extra
inbound ports), SQLite backend, data on tank/platform/forgejo.
Each stack ships a committed .env.example (real .env is git-ignored) and the
stacks README documents bring-up order, secrets, and verification.
Also add root CLAUDE.md so fresh sessions orient from files cheaply:
philosophy, doc pointers, naming quick-ref, repo map, commands, agreements.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Idempotent host configuration targeting Ubuntu 24.04 with ZFS on a
dedicated second disk. Role-based platform-infra/ansible:
- base: apt packages, timezone, unattended security upgrades.
- zfs: install ZFS, create pool on a dedicated disk (guarded against
wiping a non-empty disk), create platform datasets + customers parent
per docs/03; docker dataset mounted at /var/lib/docker.
- docker: Docker Engine + Compose plugin, daemon.json written before first
start so the native zfs storage driver initializes on the ZFS data-root;
per-site network address pool preconfigured.
- firewall: nftables inbound default-deny in a dedicated table that never
flushes Docker's rules; container outbound SMTP blocked via a DOCKER-USER
jump applied by a systemd oneshot.
- ssh_hardening: key-first SSH with an anti-lockout assertion, config
validation gate, and the sftponly group for Phase 4 SFTP accounts.
Includes ansible.cfg, requirements.yml, inventory example, group_vars with
safety notes, and a run guide. Real inventory (hosts.yml) is git-ignored.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Establish the design foundation for the heleosv2 multi-tenant hosting
platform before any implementation code:
- Monorepo skeleton: docs/, platform-infra/, site-templates/,
deployments/, control-panel/ with orientation READMEs.
- docs/: roadmap index, architecture + threat model, naming conventions,
site profiles, provisioning workflow, backup & DR runbook, repo/GitOps
layout, and the approved architecture plan.
- docs/adr/: 9 ADRs recording the rationale for single-host Compose,
Traefik edge, nginx+fpm split, shared MariaDB, ZFS-per-customer,
decoupled backup streams, Forgejo, CLI-first, and SFTP-only.
- Secrets hygiene: .gitignore (only *.enc.* committed) and .gitattributes
(LF for scripts/Dockerfiles/YAML run on the Linux host).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>