As of August 2026, the Ansible ecosystem is split between two distribution paths:
<strong>EOL versions as of August 2026</strong>: Ansible 10--12 and ansible-core 2.15--2.18 are EOL. Do not use these in new work. Only Ansible 13.x / ansible-core 2.20 is current; 14.0.0/2.21 is in development.
<strong>Molecule has been removed from the active workflow as of August 2026.</strong> It no longer serves as a testing target or standard. All role validation now uses:
Existing Molecule test harnesses (in ansible/collections/ansible\_collections/lab/franklin/roles/ role /molecule/}) remain as historical artifacts but are no longer invoked or maintained. New roles must use native ansible-test exclusively.
The lab/franklin Ansible collection lives at:
\begin{lstlisting}[style=mystyle]
/mnt/clusterfs2/workspace/lab-franklin/ansible/collections/ansible_collections/lab/franklin/
\end{lstlisting}
It is the central automation artifact for the entire lab.bitsmasher.net infrastructure, managing provisioning, configuration, and testing across all hosts.
The collection follows standard Ansible Galaxy conventions with namespace lab} and name franklin}:
The collection contains roles organized under the roles/ directory. As of August 2026, the following roles are maintained with native ansible-test harnesses:
\begin{lstlisting}[style=mystyle]
roles/
├── apt_mirror/ # APT mirror configuration
├── beagleboard/ # BeagleBoard device provisioning
├── common/ # Shared base configuration (bootstrap.sh, common utils)
├── container_registry/ # Docker/container registry setup
├── ctfd/ # CTFd platform deployment
├── desktop/ # Desktop environment provisioning
├── docker/ # Docker engine installation
├── documentation/ # Documentation generation roles
├── dhcp/ # DHCP server configuration (dhcpd.conf)
├── dns/ # BIND/named DNS server
├── golang/ # Go language toolchain
├── jetson-nano/ # NVIDIA Jetson Nano provisioning
├── k3s_agent/ # k3s worker node
├── k3s_server/ # k3s control plane server
├── k8s/ # Kubernetes generic setup
├── latex/ # LaTeX environment
├── logging/ # Centralized logging
├── music/ # Music service configuration
├── nfs/ # NFS server/client
├── nix/ # Nix package manager
├── openbsd/ # OpenBSD host bootstrap (blowfish target)
│ ├── tasks/main.yml # Includes files.yml, timezone setup
│ └── files/ # bootstrap.sh, login.conf, hosts entries
├── paloalto/ # Palo Alto firewall config
├── prereq/ # Prerequisite packages
├── pypi_internal/ # Internal PyPI repository setup
│ ├── tasks/main.yml # Symlink pypi_dir -> html_dir/pypi
│ └── vars/main.yml # html_dir=/var/www/html, pypi_dir=/mnt/storage1/LAB/pypi
├── python/ # Python environment
├── raspberrypi/ # Raspberry Pi provisioning
├── samba/ # Samba file sharing
├── security/ # Hardening and security baselines
├── shell/ # Shell configuration
├── ssh/ # SSH server hardening
├── tls/ # TLS certificate management
├── chonk/ # chonk-specific configuration
├── cluster/ # Cluster-wide settings
\end{lstlisting}
The full project layout:
\begin{lstlisting}[style=mystyle]
/workspace/lab-franklin/
├── Makefile.am # Top-level autotools target (Python, Docker, dev)
├── configure.ac # Autotools config (autoconf)
├── bootstrap.sh # Cross-platform bootstrapper
├── network_update.sh # One-shot maintenance script (hardened 2026-08-02)
├── ansible/ # ANSIBLE_HOME root
│ ├── playbook.yml # Main playbook
│ ├── hosts # Inventory file
│ └── collections/ # Collections path
│ ├── ansible_collections/ # Galaxy namespace
│ └── lab/franklin/ # The lab/franklin collection
│ ├── galaxy.yml
│ ├── roles/ # All role modules (37 total)
│ └── docs/latex/ # LaTeX docs build (autotools)
├── container/ # Docker/Podman configs
├── terraform/ # Terraform infrastructure definitions
├── bin/ # Shared utility scripts (common.sh)
└── docs/manual/ # Lab manual documentation (this document)
\end{lstlisting}
The project uses GNU Autotools for the root-level build:
The network\_update.sh} script was hardened on 2026-08-02 with: set -euo pipefail, auto-resolved paths, pinned K3s version validation, ANSIBLE_HOME environment checks, removed dangerous clush/apt-get blast radius, and dead code removal. It runs the main playbook at ansible/playbook.yml} as its primary action.
The stargate host (10.10.16.66, Debian 12 bookworm) serves as the exclusive testing platform for the lab/franklin Ansible collection. All test execution runs with zero external billing -- direct shell access to the gateway sandbox eliminates API token overhead.
Native ansible-test} has replaced Molecule as the canonical testing framework:
The legacy Molecule directory structure at each role's molecule/} subdirectory has been deprecated. It remains in-place as historical reference but is excluded from CI pipelines and the Makefile.am test target.
For a given role, the native test cycle is:
From stargate's workspace:
\begin{lstlisting}[style=mystyle]
cd ~/workspace/lab-franklin/ansible/collections/ansible_collections/lab/franklin
ansible-test sanity
ansible-test integration dns --python 3.12
ansible-playbook --syntax-check playbook.yml
\end{lstlisting}
Roles use dynamic file inclusion via templated variables to avoid hardcoded paths:
This convention eliminates static path dependencies across role invocations and supports multi-target deployment without playbook-level path overrides.
The following deprecated utilities have been removed from baseline role manifests:
Static inventory reachability assumptions have been replaced with non-interactive batch probes:
\begin{lstlisting}[style=mystyle]
ssh -o BatchMode=yes -o ConnectTimeout=3 -i ~/.ssh/id_ed25519_openclaw franklin@<host> "hostname && whoami"
\end{lstlisting}
This approach eliminates stale host entries, prevents password prompts in automation pipelines, and provides deterministic connectivity feedback with a 3-second timeout. Never assume reachability from static notes -- always probe on demand.