Commit Graph
36 Commits
Author SHA1 Message Date
kevinveenbirkenbach aa55c8c8bd Merge branch 'main' into feature/role-stalwart-mail-swarm 2026-10-02 17:40:09 +02:00
kevinveenbirkenbachandClaude Opus 5.5 5e0bcee6f5 docs(agents): point the Assumption 10 link at its current heading
7ed60e1fc2 renamed the Assumption 10 heading in security.md, and the
link in sandbox.md still used the short #assumption-10 fragment, which
no heading slug matches. It now carries the full slug of the new
heading, the form 4efa892706 on feature/store-i18n uses for the old
one, so that branch's merge resolves the line by keeping main's side.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 16:57:31 +02:00
kevinveenbirkenbachandClaude Opus 5.5 4e1c8e2de1 fix(agents): deny rm -rf and the host credential files in the sandbox
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>
2026-09-30 16:57:30 +02:00
Alejandro Roman 04a657649c Merge remote-tracking branch 'upstream/main' into feature/role-stalwart-mail-swarm 2026-09-23 20:41:12 +04:00
kevinveenbirkenbachandClaude Opus 5.5 f47ca6e7c0 chore(agents): deny git commit --amend
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>
2026-09-23 15:49:02 +02:00
Alejandro Roman 9df3287adf Merge remote-tracking branch 'upstream/main' into feature/role-stalwart-mail-swarm 2026-09-22 12:16:44 +04:00
kevinveenbirkenbachandClaude Opus 5 f40d627a1b docs(agents): merge the tool-specific files into AGENTS.md
CLAUDE.md and GEMINI.md existed as per-runtime extensions of AGENTS.md, and
only one of them carried anything at all: GEMINI.md stated that it had no
additions, while CLAUDE.md held rules that bind every runtime anyway — the
permission announcement, the make-over-raw preference, the loop, chain and
heredoc bans, the stream-the-log-to-/tmp rule, the CI-evidence rule, the push
ban and the comment rule. A rule that binds every agent does not belong in one
agent's file. AGENTS.md absorbs them and now states that no tool-specific
extension file exists; agents whose runtime auto-loads a different filename
reach it through their own configuration instead.

The filenames were machinery as much as prose. The PR templates, the branch
and pull-request scope tables, the settings and security docs that justified
the loop deny by "the textual CLAUDE.md rule", six requirement documents whose
push instruction linked ../../CLAUDE.md, and a test docstring citing the
shell-loop ban all named them, and now name AGENTS.md.
scripts/meta/resolve/pr/scope.sh deliberately keeps matching CLAUDE.md and
GEMINI.md: this change deletes them, and without those patterns the change
itself would not classify as agent-scope.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 22:51:04 +02:00
Alejandro Roman 4b9a834546 Merge remote-tracking branch 'upstream/main' into feature/role-stalwart-mail-swarm 2026-09-21 03:25:20 +04:00
kevinveenbirkenbachandClaude Opus 5 af605b049f refactor(domains): give local testing a .test domain, leave .example to docs
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>
2026-09-02 13:10:34 +02:00
kevinveenbirkenbach f99ce04ddb docs(mcp): record when a server may act for the caller
The schema lint demands a delegation block whenever a role claims to act on the
caller's behalf, pinning refresh, revocation and audience binding at one exact
version, because a rendered deployment bearer is a service account whatever the
metadata says. The lint can reject a missing block but cannot say why those
three facts and no others, so the design note carries that.

The requirement is brought up to date, and the agent cheatsheet gains the two
worktree skills.
2026-08-06 21:31:20 +02:00
Alejandro Roman f9319863cf removed guras and make local/CI DinD use .test isntead of .example for the whole stack 2026-07-08 10:11:17 +02:00
kevinveenbirkenbachandClaude Opus 4.7 c02229b4be refactor(make): align targets to domain prefixes (compose-, network-, etc.)
Apply a consistent naming convention to the Make target inventory so
every target is grouped by its domain:

- compose-*  (compose-up/down/restart/stop/exec/inner-run)
- network-*  (network-dns-setup/remove, network-ipv6-disable/restore,
              network-refresh, network-trust-ca)
- security-* (security-apparmor-restore/teardown)
- diagnose-* (diagnose-network, diagnose-disk-usage)
- fix-*      (fix-chmod, fix-dockerignore)
- clean-*    (clean-cache, clean-pycache-dirs)
- install-*  (install-agent)
- meta-*     (meta-list, meta-tree, meta-mig)

Drop the stale debug-network target and reintroduce its functionality
as diagnose-network. Cascade renames through:

- Makefile (.PHONY entries, prereqs, recursive $(MAKE) calls)
- contributor docs, agent-iteration docs, role-level docs
- bundle README, environment scripts, install/lint/test wrappers
- AGENTS.md, CLAUDE.md, compose/cache.override.yml, .github/workflows

Historical entries under docs/requirements/ and changelog/ stay
untouched as audit logs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 21:00:45 +02:00
kevinveenbirkenbachandClaude Opus 4.7 925dc830a1 chore(claude-settings): collapse Bash allow list to Bash(*) wildcard
Replaces 15 per-shape Bash entries that existed as sandbox auto-allow
workarounds with a single Bash(*) wildcard. Gating responsibility moves
entirely to ask (mutating gh/docker/git verbs) and deny (structurally
destructive shapes). Documentation updated across settings.md,
security.md, and sandbox.md to reflect the new model; em-dash clause
separators removed throughout per documentation.md style rules.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 00:30:35 +02:00
kevinveenbirkenbachandClaude Opus 4.7 162b152a0d chore(claude-settings): generalize python allowlist to multi-env, python/python3
Replaces narrow `Bash(PYTHONPATH=* python *)` with `Bash(*=* python* *)`
so test invocations carrying multiple env-var assignments
(e.g. `PYTHON=python3 PYTHONPATH=. python3 -m unittest ...`) and
`python3` binaries no longer trip the auto-allow heuristic. Sandbox
remains the primary containment layer, so widening the allow shape
adds no new blast radius.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 19:03:29 +02:00
kevinveenbirkenbachandClaude Opus 4.7 2abfc64636 chore(claude): broaden make allow shapes, per-domain WebFetch, deny git -C
Three related settings.json changes:

1. Replace narrow `Bash(*=* make *)` with three broader rules covering
   any prefix + make, plus composites with `> /tmp/*` redirect and
   `2>&1` stderr-merge. Needed so multi-env invocations with quoted
   multi-word values (e.g. `FOO=a BAR=b APPS="x y z" make target > /tmp/log 2>&1`)
   auto-allow without prompting — the prior single-env rule and the
   generic `* > /tmp/*` rules did not compose for this shape.

2. Replace inert `WebFetch(domain:*)` with explicit per-domain entries
   for `github.com` and `raw.githubusercontent.com`. Tested empirically:
   the wildcard form does not suppress the permission prompt, so a
   per-domain allowlist is the only mechanism that works.

3. Add `Bash(git -C *)` to deny. Blocks cross-repo escapes via the
   `-C <path>` flag, which otherwise lets the agent reach into other
   working trees under `additionalDirectories`. Operators needing a
   second repo must open a dedicated session there.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-19 03:27:04 +02:00
kevinveenbirkenbachandClaude Opus 4.7 1de1bf9b1d chore(claude): allow env-prefixed make, add nektosact domain
Add `Bash(*=* make *)` so `FOO=bar make <target>` invocations (notably
`make act-workflow` with ACT_WORKFLOW/ACT_MATRIX overrides) auto-allow
without prompting — mirrors the existing `PYTHONPATH=* python *` entry.
Add `*.nektosact.com` to sandbox network allowedDomains so `act` (the
local GitHub Actions runner invoked by `make act-workflow`) can reach
its update/catalog endpoint.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 12:00:12 +02:00
kevinveenbirkenbachandClaude Opus 4.7 980ab3de5e chore(claude): grant gh cache write, generic /tmp redirect allow
Add ~/.cache/gh to sandbox allowWrite so gh CLI no longer needs an
XDG_CACHE_HOME=/tmp/... prefix, and replace the five XDG-prefixed allow
entries with two generic `* > /tmp/*` redirect entries that cover any
command capturing output into /tmp. Sync sandbox.md / settings.md /
security.md and normalize the allowWrite path enumerations.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 11:25:16 +02:00
kevinveenbirkenbachandClaude Opus 4.7 9e0a7778e0 chore(claude): generalize gh run download, gate update-config via ask
Replace the one-off literal allow entry for a specific `gh run download`
invocation with the `XDG_CACHE_HOME=* gh run download *` wildcard that
mirrors the existing `view`/`list` env-prefix precedents, and move the
`Skill(update-config*)` wildcard from `allow` to `ask` so settings-file
mutations (permission rules, hooks, sandbox scope) always prompt — the
skill can otherwise self-escalate the agent's own permission surface.
Document both entries in settings.md with matching Env-Prefix and
Privilege-Escalation rationales.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 23:50:34 +02:00
kevinveenbirkenbach e32fb87f95 chore(claude): allow ~/.ssh read so git push works on ask
Removes ~/.ssh from sandbox.filesystem.denyRead so SSH auth (keys, known_hosts, config) succeeds inside the sandbox. Bash(git push*) stays in ask, which becomes the effective control point for pushes.

Documents the trade-off:
- security.md: updates Assumption 9 and adds Assumption 10 covering why ~/.ssh is readable, the cost, and the mitigations (passphrase-protected keys, key rotation on incident, settings.local.json opt-out, SSH-agent-socket alternative).
- sandbox.md: removes ~/.ssh from the Read Restrictions table and links back to Assumption 10.
2026-04-17 23:20:15 +02:00
kevinveenbirkenbachandClaude Opus 4.7 2a1dfc04e3 chore(claude): generalize gh run/issue allow patterns
Collapse six literal XDG_CACHE_HOME=/tmp/gh-cache gh run/issue list
allow entries into XDG_CACHE_HOME=* gh run list *, XDG_CACHE_HOME=*
gh issue list *, gh run *, and gh issue *. Plain gh run * / gh issue *
are safe because every mutating verb is caught by gh * <verb>* ask or
gh issue transfer* deny. Env-prefix variants stay narrow (no broader
than list) because env prefix bypasses subcommand ask/deny patterns.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 21:27:10 +02:00
kevinveenbirkenbachandClaude Opus 4.7 297e8e0769 chore(claude): allow gh api *, ask mutation flags
Allow read-only gh api GETs (plus XDG_CACHE_HOME=* prefix variant) so
log-fetch invocations with env prefix + redirect stop prompting. Route
every implicit-POST shape through ask: -f/-F/--field/--raw-field/--input
body flags and graphql*. Path-based deny rules keep precedence.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 21:27:10 +02:00
kevinveenbirkenbachandClaude Opus 4.7 a7cd24b9cb docs(actions): add SPOT catalog and push-trigger-pull procedure
Centralise the full GitHub Actions workflow catalog in
docs/contributing/tools/actions.md as the single source of truth, link
pipeline.md and workflow.md to it, and introduce
docs/agents/action/push-trigger-pull.md as the authoritative procedure
for the push to draft CI to ready-for-review loop. Retire the stale
flat docs/contributing/tools/agents/claude.md and drop docs README
page-inventories that violate the readme_md.md scope rule.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 17:40:28 +02:00
kevinveenbirkenbachandClaude Opus 4.7 75d229a5f1 chore(claude): allow PYTHONPATH=* python *
Mirrors the XDG_CACHE_HOME=* gh run view * precedent: leading env-prefixes fall out of the auto-allow heuristic. Narrow namespace, sandbox boundaries still bound every write / read / network call.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 14:12:24 +02:00
kevinveenbirkenbachandClaude Opus 4.7 ed34db2e8c chore(claude): allow tail *
Parallel to head *: tail is the right-hand-side twin that lives after && / | and would otherwise decline auto-allow.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 14:12:23 +02:00
kevinveenbirkenbachandClaude Opus 4.7 77bd915195 chore(claude): allow pkill *, forbid ; statement chains
- Allow Bash(pkill *) so host-process termination (stuck ansible/make runs) does not prompt
- Add explicit CLAUDE.md rule forbidding ; multi-statement chains (same root cause as loop rule: auto-allow heuristic declines compound lines)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 13:09:06 +02:00
kevinveenbirkenbachandClaude Opus 4.7 ff7512c1bc docs(claude): add security.md, allow docker *, gate docker synonyms
- Allow Bash(docker *) under test-only host assumption; keep run/build/push/login in ask
- Catch synonyms: docker container run, docker image build, docker buildx build, docker image push
- New security.md enumerates 9 trust assumptions as single source for rationale
- Shorten settings.md rationales to link by assumption number (DRY)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 11:39:14 +02:00
kevinveenbirkenbachandClaude Opus 4.7 76ad0e7081 chore(claude): refine Bash permissions (echo/head allow, loops deny, drop /tmp/* scoping)
- Add Bash(echo *) and Bash(head *) to allow list for chained stdout/read primitives.
- Move Bash(for *)/Bash(while *)/Bash(until *) to deny: hard-enforces the
  CLAUDE.md loop-forbidden rule so violations fail fast and force refactor
  to single-command / grep / xargs forms.
- Drop redundant /tmp/* scoping from Bash(XDG_CACHE_HOME=* gh run view *)
  and Bash(tee *); sandbox.filesystem.allowWrite is the authoritative
  write boundary, so path restrictions on the allow rule added only
  maintenance, no safety.
- Update docs/contributing/tools/agents/claude/settings.md to match.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 09:47:17 +02:00
kevinveenbirkenbachandClaude Opus 4.7 918d560587 chore(claude): allow Bash(make *) and Bash(tee /tmp/*)
Why: `make` invocations and `… 2>&1 | tee /tmp/<log>` pipelines regularly
fall outside the sandbox auto-allow heuristic and trigger unnecessary
approval prompts. Adding targeted allow entries removes the friction
while keeping the write scope of `tee` bounded to /tmp.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-17 09:12:39 +02:00
kevinveenbirkenbachandClaude Opus 4.7 cb3d0399bb chore(claude): add gh run list + grep + wc allows, sort permissions.allow
Add explicit Bash allow entries for command shapes that slip past the
sandbox auto-allow heuristic: `gh run list *` (jq pipes in --jq string
confuse the parser), `grep *` (to back the new CLAUDE.md rule against
for-loops around per-file greps), and `wc *` (read-only, any flag).

Reorder permissions.allow strictly ASC so the Bash entries live
alongside the other allow rules in sort order, matching how ask/deny
are laid out. Document the three new entries in settings.md and add
the "prefer flat commands" guidance to CLAUDE.md Code Execution.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-16 23:45:01 +02:00
kevinveenbirkenbachandClaude Opus 4.7 7e246e0806 chore(claude): allow gh run view + wc -l bash workarounds
Add explicit Bash allow entries for command shapes where sandbox
auto-allow does not fire reliably (env-variable prefix, output
redirect, &&-chained subcommands). Document the rationale in the
settings reference so future additions follow the same pattern.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-16 23:45:00 +02:00
kevinveenbirkenbachandClaude Opus 4.7 ee30f6d446 chore(claude): tighten gh permission rules and lint settings.json sort order
Consolidate gh ask/deny entries via namespace-wildcard patterns, cover
compound subcommands (item-*, field-*, mark-template*), and add a lint
test that enforces ASCII-sorted permission and sandbox arrays in
.claude/settings.json.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-16 21:47:14 +02:00
kevinveenbirkenbachandClaude Opus 4.7 3375498119 chore(claude): drop redundant Bash(curl*) ask rule
Sandbox network gate already bounds curl to allowedDomains, so the
interactive prompt added no real protection on top. Remove the rule
and the matching docs row.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-16 19:33:38 +02:00
kevinveenbirkenbachandClaude Opus 4.7 4e1be21da3 chore(claude): generalize sandbox allowedDomains via subdomain wildcards
Collapse explicit per-host entries into *.parent.tld patterns where the
sandbox only needs reach to a single domain family (Ansible, AWS, Docker,
Gitea, GitHub + githubusercontent, gstatic, hCaptcha, infinito, npm,
pythonhosted, Taiga). Apex domains stay listed bare. Sorted ASC with the
wildcard block first.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-16 19:32:07 +02:00
kevinveenbirkenbachandClaude Opus 4.7 764060c793 chore(claude): expand sandbox network/write paths, ignore host artefacts
Network gate does not honor wildcards, so allowedDomains is enumerated
explicitly for every host the agent + tooling actually need. Adds
~/.cache/pip, ~/.venvs, ~/.ansible to allowWrite so the pre-commit
make test hook can install the editable package and run ansible.

Adds host shell / editor / tooling state files to .gitignore (and the
auto-generated .dockerignore mirror) so they never become part of the
working tree. Switches the dependabot coverage test to git ls-files
with an os.walk fallback that derives its skip list from the live
.gitignore, so it stays correct in container test environments
without .git/ while still ignoring untracked host artefacts.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-16 19:24:31 +02:00
Environment TestandClaude Opus 4.6 e2dbbceb6a chore(sandbox): add cross-distro installer for bwrap/socat via make agent-install
Adds scripts/install/sandbox.sh, a distro-aware installer for the OS-level
dependencies the Claude Code sandbox backend needs (bubblewrap, socat),
and exposes it through a new `make agent-install` target. Documents the
target in docs/contributing/tools/make.md, scripts/install/README.md, and
the Claude sandbox guide so contributors on a fresh host can bring up a
working sandbox without hand-installing distro packages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 18:06:57 +02:00
Environment TestandClaude Opus 4.6 65cf9caff7 chore(claude): restructure agent docs, harden sandbox config, update settings
- Move docs/contributing/tools/agents/claude.md to claude/settings.md
- Split sandbox explanations into claude/sandbox.md, add claude/README.md as index
- Enable OS-level sandbox ("sandbox.enabled: true") for project-wide filesystem isolation
- Fail-closed on missing backend ("sandbox.failIfUnavailable: true") to prevent false-security state when bwrap/sandbox-exec is absent
- Add deny rules: "git clean*", "git branch -D*"
- Remove redundant "Read(~/.cache/pre-commit/**)" allow entry
- Update CLAUDE.md to instruct agent to summarize live permission state from settings.json at session start
- Update AGENTS.md and common.md references to new doc paths
- Normalize .md formatting per documentation.md rules (em dash removal, headline emojis)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 16:55:45 +02:00