- Shell 55.7%
- PowerShell 15.7%
- Python 14.5%
- Jinja 10.1%
- Makefile 2.3%
- Other 1.7%
Bring in agent-foundry 7e46402..fef5993 by three-way merge: the foundry-upgrade skill and its .claude/skills link, the optional Memory triggers section of AGENTS.local.md in the Project memory rules, and the curator's no-write for candidates outside declared triggers in docs/LOCALRECALL.md. The skill's Record now names the source and adopted commit, so the README credit keeps the attribution without the commit and the template lifecycle rule points at the skill for re-adoption. |
||
|---|---|---|
| .agents/skills | ||
| .claude | ||
| .codex/agents | ||
| docs | ||
| group_vars | ||
| plugins/cache | ||
| roles | ||
| scripts | ||
| tests | ||
| .ansible-lint | ||
| .gitignore | ||
| .yamllint | ||
| AGENTS.md | ||
| ansible.cfg | ||
| CLAUDE.md | ||
| CONTRIBUTING.md | ||
| COPYING | ||
| handbook.yml | ||
| Makefile | ||
| os-update.yml | ||
| reachable.yml | ||
| README.md | ||
| requirements-dev.txt | ||
| requirements.yml | ||
| ssh-copy-id.yml | ||
castrum
Hardened baseline for mixed client fleets: one tested role library, per-tenant inventories, applied in rolling batches.
A Roman castrum was built to the same plan wherever the legion stopped — same layout, same gates, same defences, on any ground. This repository is that plan for a fleet of machines: the same hardening, firewall and service configuration applied identically across every site it manages, varied only where the ground demands it.
Each site's inventory lives outside this repository. What is here is what a group means, the roles that implement it, and the tests that prove they do.
Goal
castrum gives every host in a fleet the same hardening, firewall, services,
sysadmin tooling and updates, varied only by the groups a host belongs to
and its own host variables, never by editing a playbook. handbook.yml and
os-update.yml apply it in widening batches, one host first, so a bad
change breaks one machine while the rest are still untouched.
Scope is GNU/Linux, Debian family, plus Windows 11 workgroup PCs over OpenSSH: their hostname, a hardening baseline, the Windows firewall and Windows Update. castrum is not a provisioning tool — it configures machines that already exist — and not a multi-OS abstraction.
Quick start
-
Clone the repository and build the toolchain (needs Python 3.12+):
git clone https://git.unitoo.it/claudiomaradonna/castrum.git && cd castrum make setup && . .venv/bin/activate -
Create
inventory/<tenant>/hosts.ymlwith one host inmanaged, the group every playbook targets:managed: hosts: web1: ansible_host: 192.0.2.10 # webservers: # a host joins as many groups as apply # hosts: # web1: -
Install your key once, with password authentication this one time (needs
sshpasson your machine and a host that accepts a root password login once):ansible-playbook ssh-copy-id.yml -i inventory/<tenant>/ -k \ -e ssh_user=root -e "ssh_copy_id='$(cat ~/.ssh/id_ed25519.pub)'"castrum connects as
root(ansible_useringroup_vars/all.yml); a tenant that connects as someone else setsansible_userin its hosts'host_vars(Per-host settings).A Windows PC also joins
windows—managedalone sends it the Linux play — and runsscripts/bootstrap-windows-ssh.ps1once, at the PC, instead of this step; setansible_userin itshost_varsto the account that script creates (Windows PCs). -
Dry run:
ansible-playbook handbook.yml -i inventory/<tenant>/ --check --diff -
Read the diff, then run the same command without
--check.
Pass the tenant directory, never inventory/ itself, which would merge
every tenant into one run
(Tenants and inventories).
Common usage
handbook.ymlapplies the baseline: hardening, firewall, the service roles a host's groups switch on, and sysadmin tooling.os-update.ymlupdates packages, or runs Windows Update;-e os_update_auto=rebootorshutdownacts when an update wants a reboot.ssh-copy-id.ymlinstalls a public key for a user. Linux only.
ansible-playbook handbook.yml -i inventory/<tenant>/ # the whole tenant
ansible-playbook handbook.yml -i inventory/<tenant>/ -e target=web1 # one host
ansible-playbook handbook.yml -i inventory/<tenant>/ --tags firewall # one concern
ansible-playbook handbook.yml -i inventory/<tenant>/ --tags zram # one role
ansible-playbook os-update.yml -i inventory/<tenant>/ -e os_update_auto=reboot
Two modes are passed with -e, for one run only
(Modes):
update_only=trueskips the install-and-configure work of the roles that implement it.uninstall=truedeletes the castrum firewall table until the next ordinary run.
Documentation
- docs/ADVANCED_USAGE.md — running it in full: groups, tags, modes, Windows, first runs, updating.
- docs/ARCHITECTURE.md — how it is built, and the list of roles.
- CONTRIBUTING.md — changing it, and the gates.
- Each role's
README.md, underroles/— what that role does. - AGENTS.md — instructions for coding agents.
License
GNU General Public License, version 3 or later (GPL-3.0-or-later); see COPYING.
Agent policy seeded from Agent Foundry by Claudio Maradonna.