Takes the agent policy from the feature/store-i18n worktree over unchanged:
- rm -rf* leaves the deny list.
- Edit and Write on any .claude/*.json now ask for confirmation.
- The sandbox network allowlist trades the single 127.0.0.6:8097 entry for 127.0.0.1 to 127.0.0.12 and localhost on every port, so sandboxed commands reach local services and published compose ports.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The committed settings enabled the caveman and ponytail plugins from two
unpinned GitHub marketplaces for everyone who trusts the repository, and
plugin hooks run in the harness, outside the Bash sandbox. The block is
a leftover: since kevinveenbirkenbach/skills@3be5655b44, make
install-skills writes it to .claude/settings.local.json, so enabling the
plugins stays a per-developer choice.
Found while reviewing the settings after Hanno Horrmeyer's report; see
7ed60e1fc2.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An external report flagged the Bash(*) allow in the committed
.claude/settings.json. Bash(*) stays: every command still runs inside
the OS sandbox, and deny and ask take precedence over it. Reviewing the
file against its own documentation turned up two real gaps instead.
security.md and settings.md cite an rm -rf* deny rule as the guard
against `rm -rf .` wiping the worktree and .git/, but the rule was lost
in ee30f6d446 when the deny list was rewritten. It is back.
denyRead covered ~/.aws, ~/.config/gcloud, ~/.gnupg and ~/.kube only.
~/.docker/config.json holds registry logins inline, ~/.git-credentials
and ~/.netrc hold plaintext credentials, and the default ~/.ssh/id_*
keys were readable because Assumption 10 kept ~/.ssh open for an
ask-gated git push that has since moved to deny. All nine paths are
denied now. The rest of ~/.ssh stays readable, so SSH fetches keep
known_hosts and authenticate through the agent socket. Sandboxed docker
calls pull anonymously and print a config warning on stderr.
Thanks to Hanno Horrmeyer for the report.
Reported-by: Hanno Horrmeyer
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An agent cannot see whether the operator committed on top of its last
commit in the meantime. An amend then rewrites a commit it never
authored: it happened to a signed, tagged release commit, which only a
soft reset onto the original recovered. Follow-up changes land as a new
commit instead.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
RFC 6761 reserves .test for testing and .example for documentation, and the
project's own domain was doing both jobs under the documentation name. It is
what default.env hands every local deploy and what the test fixtures assert
against, so it is a testing name.
Rename it wherever it stands for the running machine: the environment
defaults, the development inventory, the workspace and trust scripts, the test
suites, and the four documents that describe real behaviour - the DNS mapping
to loopback, the browser CA trust, WSL2 resolution. Those documents would have
started lying the moment default.env changed.
The sandbox allowlist moves with them, since egress is matched per pattern and
nothing resolves the old name any more, and the sandbox document that mirrors
that list follows it.
Two places keep .example on purpose: the link-style sample in the
documentation guide, which is documentation about documentation, and every
generic fixture name elsewhere in the suite - a.example, foo.example and their
kin. Those are a larger sweep of their own and are not touched here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A second branch needs its own checkout to be worked on in parallel, with its own
slot, subnet, ports and container names so two deploys do not take each other's.
The two skills route to the make targets that create and release one.
The sandbox gains the worktree directory as a writable path, without which the
checkout cannot be created and the skills are unusable. It is the only change
to the permission file: one write path, no deny or ask rule touched.
Bash(git commit*) sits in ask and Bash(git push*) in deny, but both match on
the argv prefix: 'git --git-dir=<path> commit' and 'git --work-tree=<path>
push' start with neither, so they slipped straight through. The confirmation
dialog never appeared for a commit issued that way.
Deny both flags outright. Repo-local work has no need for them - the worktree
tooling covers the legitimate cases - and leaving them open makes every other
git rule advisory.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Verifying the swarm lab's dnsmasq behaviour meant reading the shipped config and systemd unit out of the real packages of all five distros, which needs the arch package archive and the centos stream mirror alongside the ubuntu and debian hosts already allowed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Grants accumulated during research sessions. They landed in the tracked
settings file because .claude/settings.local.json was not writable, so
the documented destination for runtime approvals was unavailable.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
packages.ubuntu.com serves the Ubuntu leg of the external package availability
test, next to the packages.debian.org entry that is already allowed.
pkgs.alpinelinux.org and pkgs.org are general package searches with no caller
in the repository.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reading the packaged nfs-ganesha spec is how the Fedora build of the
package was checked against its libntirpc dependency. Add the domain to
the WebFetch allowlist so that lookup does not need an approval prompt.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Invert the .claude ignore from a per-file denylist to an allowlist so new tool state never leaks into the repo; only settings.json and .gitignore stay tracked.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Release metadata stays a manual operator step, mirroring the same ask-gate in backup-docker-to-local.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Run the python suites through pytest-xdist when pytest is importable
(id-level collection parity with unittest discover verified per suite;
serial unittest stays the fallback), with -rP so warnings printed by
passing tests, like the update annotations, still reach the log. run.sh
self-provisions the dev extras through scripts/install/dev-extras.sh,
which stamps inside the venv and skips read-only venvs; make onboard
pre-warms the same path in the running container.
Hoist per-test cache resets and roles scans to setUpClass in the lookup
and resolvable suites (a per-test fingerprint-memo reset closes an
id()-reuse cache-poisoning flake the hoist exposed), inject generation
names into the nfs backup script test instead of sleeping, and narrow
ansible-lint scope to real ansible content. ansible-lint additionally
sits behind a content fingerprint gate that skips the run when nothing
it walks has changed; without a usable git (container bind mounts) the
gate disables itself and the full run remains.
Drop the import-linter layer and ignore for the deleted deps module;
the contract now locks compose -> common -> env. Measured on the host:
unit 101s to 20s, integration 90s to 24s, lint tests 43s to 19s,
repeated ansible-lint 53s to 0.4s.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
make install-skills now registers the caveman and ponytail marketplaces
and enables both plugins in the project settings, so their SessionStart
hooks auto-activate token-compressed communication (caveman) and
minimal-code mode (ponytail) for every agent session in this repository.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds frappe.io, discuss.frappe.io, docs.zammad.org, www.kixdesk.com, web.archive.org, amble.blog to the WebFetch allowlist (surfaced while researching upstream memory requirements for the container-worker resource limits).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
autoformat reformats (settings sort, complexity/model.py, test_database_query.py, keycloak vars wrap); shorten akaunting swarm task name to <=80 chars; delete narration comments from keycloak vars; remove 'invariant' from the comment linter's valid marker set. make autoformat + make test verified green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add forum.keycloak.org, docs.redhat.com, git.coopcloud.tech and synacktiv.com to the WebFetch allowlist used during swarm/collabora/keycloak research.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract the csp, domains, and networks blocks out of roles/*/meta/server.yml
into dedicated meta files, each mapping to its own top-level applications key
via the recursive meta-walk:
csp: -> meta/csp.yml -> applications.<app>.csp
domains: -> meta/domains.yml -> applications.<app>.domains
networks: -> meta/networks.yml -> applications.<app>.networks
Residual nginx/proxy keys (status_codes, client_max_body_size, locations,
ip6_enabled, config_upstream_url) stay in server.yml; an emptied server.yml is
deleted. 98 server.yml -> 249 new files, 74 deleted.
- Consumers repointed applications.<app>.server.{csp,domains,networks} ->
applications.<app>.{csp,domains,networks} across templates, vars, tasks,
lookups and filters.
- Infra readers migrated: canonical_domains_map, application_domain_index,
build_ldap_role_entries, service-registry overlay discovery,
roles_with_service, and the file-based readers utils/domains/list.py +
utils/meta/scan.py (read the now-deleted server.yml directly).
- Detection + mapping: _META_TOPICS gains csp/domains/networks;
ROLE_FILE_META_{CSP,DOMAINS,NETWORKS} constants + ROLE_FILES entries.
- New self-expiring lint guard (test_server_meta_no_split_fields) fails if a
server.yml still carries a split field; auto-fails from 2026-12-28 as a
delete reminder.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Added to permissions.allow during the collabora swarm-convergence investigation (Collabora SDK docs lookup for the mount_jail_tree knob).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
svc-storage-nfs-server gains a backend flavor selected by services.nfs-server.flavor (default "{{ 'ganesha' if RUNTIME in ['dev','act'] else 'kernel' }}"). The ganesha flavor installs nfs-ganesha + FSAL_VFS and renders /etc/ganesha/ganesha.conf (NFSv4, Pseudo == Path so clients mount the real paths unchanged, Root_Squash on the export base and No_Root_Squash on the state subdir), reloaded via systemctl restart. It runs in the dev/act DinD swarm-test, where the host kernel nfsd is not network-namespaced and a privileged node driving exportfs would wedge it into a D-state. The kernel flavor keeps nfs-kernel-server + exportfs -ra for production and every non-dev/act runtime. 01_core dispatches on the flavor, 03_prepare holds the shared export preparation, flavor/{kernel,ganesha}/main.yml hold the per-backend install/render/service steps, and handlers gains the ganesha restart.
docs/agents/action/iteration: role.md renamed to compose.md (Compose Loop); new swarm.md for the swarm deploy loop split out of workflow.md; workflow.md trimmed to GitHub Actions workflow iteration; all cross-references updated; the duplicated README gotcha removed; the NFS gotcha rewritten to the ganesha design.
Also drops a retired-flag history comment in group_vars/all/01_modes.yml and a redundant comment in the init selection script, and allows WebFetch(repology.org) used during ganesha packaging research.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Auto-recorded while a research agent fetched deepwiki.com; added in sorted
position in permissions.allow.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
File requirement 028 for a self-hosted web-app-atmo role (upstream
flo-bit/atmo-events) that reuses web-app-bluesky for AT Protocol
identity, plus a one-way AT Protocol -> ActivityPub event bridge to
web-app-mobilizon. Index it in the requirements README and allow
WebFetch(atmo.rsvp), used while researching the upstream project.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>