Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
071ca29
docs(readme): import the signing key before installing dagnode-release
p4nda Aug 21, 2026
27aae25
fix(bootstrap): stop capturing .nvm as an embedded git repo in the co…
p4nda Aug 21, 2026
a890da1
fix(packaging): start ai-tools-relabel.path on fresh install
p4nda Aug 21, 2026
f5c10bd
Merge pull request #32 from dag-node/feature/ATR-260821-relabel-start…
p4nda Aug 21, 2026
a4a4423
feat(cli): claim projects for another operator with --for
p4nda Aug 21, 2026
834c9cc
chore(release): 0.12.0
p4nda Aug 21, 2026
023f556
docs(cli): name the resolved owner in the --reclaim header
p4nda Aug 21, 2026
d3eb4c5
fix(cli): refuse every --for run before it prompts for a password
p4nda Aug 21, 2026
128011a
test(cli): fail fast instead of hanging if a --for refusal reaches sudo
p4nda Aug 21, 2026
4c8748a
chore(perms): align the tracked exec bit and gate it against drift
p4nda Aug 21, 2026
6091181
Merge branch 'feature/ATR-260821-claim-for-operator' into develop
p4nda Aug 21, 2026
ac9dd24
docs(rules): split claude-code specifics into a provider rule
p4nda Aug 22, 2026
b7ac713
fix(relabel): fail when an entrypoint is installed where its manifest…
p4nda Aug 22, 2026
fd556ed
test(relabel): pin the entrypoint reconciliation from both ends
p4nda Aug 22, 2026
5376e19
docs(rules): state the entrypoint reconciliation and the npm/native t…
p4nda Aug 22, 2026
91bfd65
fix(launch): verify and exec the same entrypoint inode
p4nda Aug 22, 2026
086fcf1
feat(updater): verify the agent entrypoint against its vendor's signe…
p4nda Aug 22, 2026
87ce502
feat(cli): report entrypoint verification in ai-tools --status
p4nda Aug 22, 2026
12117ac
test(launch): drive the entrypoint-pin refusal through the deployed shim
p4nda Aug 22, 2026
7a4936a
Merge pull request #33 from dag-node/feature/ATR-260822-claude-code-n…
p4nda Aug 22, 2026
0d036d9
feat(skills): ship a governance skill for systems that act with autonomy
p4nda Aug 23, 2026
81de806
docs(skills): version the governance framework as a v0.1 draft
p4nda Aug 23, 2026
e45b6a5
docs(claude-settings): state the third deny group and its criterion
p4nda Aug 23, 2026
c1e4aaa
test(hooks): pin the irreversible-VCS deny entries
p4nda Aug 23, 2026
bb1952b
docs(claude-settings): describe every default settings.json ships
p4nda Aug 23, 2026
d8a5e4d
docs(claude-md): complete the root-helper and shared-library enumerat…
p4nda Aug 23, 2026
99893d9
docs(logging): attribute a journal line by its writer's uid, not by i…
p4nda Aug 23, 2026
ac2fc34
Merge branch 'feature/ATR-260823-governance-audit-corrections'
p4nda Aug 23, 2026
ee41e22
docs(governance): state the agent's own conduct in the always-loaded …
p4nda Aug 23, 2026
af4ff7b
Merge branch 'feature/ATR-260823-agent-conduct'
p4nda Aug 23, 2026
b72d588
feat(logging): emit optional native journald fields from the shared l…
p4nda Aug 23, 2026
99595ff
feat(hooks): record every tool call in the operator-readable trail
p4nda Aug 23, 2026
bb90a8f
test(hooks): assert the tool-call record and pin its content bound
p4nda Aug 23, 2026
32fc2d1
feat(cli): add --audit, a reader for the refusal and rejection trails
p4nda Aug 23, 2026
a6a5609
docs(governance): state where the audit plane's evidence line falls
p4nda Aug 23, 2026
c473a24
Merge branch 'feature/ATR-260823-governance-audit-trail'
p4nda Aug 23, 2026
3dfdf82
fix(tests): correct the audit helper's source mode and the CLI wiring…
p4nda Aug 23, 2026
fae1d13
fix(hooks): record the whole path a command acted on
p4nda Aug 23, 2026
cb1e3a7
fix(cli): make --audit readable on a host with a recurring condition
p4nda Aug 23, 2026
ddadf64
fix(cli): report --audit findings as events, not as current state
p4nda Aug 23, 2026
3c694d8
Merge pull request #34 from dag-node/feature/ATR-260823-capable-syste…
p4nda Aug 23, 2026
55a915b
feat(cli): add a stop path that ends a running agent session
p4nda Aug 23, 2026
239314c
fix(cli): find the sessions a dot-named or threaded cgroup hid
p4nda Aug 23, 2026
4f4f5bd
fix(cli): bound the stop helper's attribution so a wedged manager can…
p4nda Aug 23, 2026
a468211
docs(stop): single-source the session-stop design and point each surf…
p4nda Aug 23, 2026
a0da54f
feat(cli): add the --stop verb, and install the helper it reaches
p4nda Aug 23, 2026
326eb77
test(cli): prove the stop from the kernel's side, the agent's side, a…
p4nda Aug 23, 2026
4550236
test(suite): bound each test file in time so a blocked one cannot sta…
p4nda Aug 23, 2026
42a04c7
feat(cli): make --stop terminate every session, and take no target
p4nda Aug 24, 2026
b6b808c
docs(stop): state the one form, and why there is no per-project one
p4nda Aug 24, 2026
aeb5b57
test(stop): invert the exemption assertions, and keep the suite off t…
p4nda Aug 24, 2026
81e64e3
fix(stop): stop prefixing every refusal with a stray file descriptor
p4nda Aug 24, 2026
75600ce
feat(stop): count agent sessions apart from the account's own plumbing
p4nda Aug 24, 2026
06fc0c5
fix(cli): refuse a --stop command line with the documented usage code
p4nda Aug 24, 2026
e5af331
fix(drill): read cgroup.procs as content, since -s is always false on…
p4nda Aug 24, 2026
57aa24d
fix(stop): keep an unpassed count from abandoning the confirmation
p4nda Aug 24, 2026
3d0c34c
docs(stop): give each fact about --stop one home, and correct two sta…
p4nda Aug 24, 2026
6c4ddf3
Merge pull request #35 from dag-node/feature/ATR-260823-governance-st…
p4nda Aug 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
329 changes: 329 additions & 0 deletions .claude/rules/agent-claude-code.rule.md

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions .claude/rules/authoring.rule.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ one-file-per-rule mapping.

- One file per component: `<topic>.rule.md`. The `.rule.md` suffix lets tooling and this
guideline target every rule with the `*.rule.md` glob.
- **A rule covering one provider is named for its kind and its name**, matching the package
taxonomy (`ai-tools-agents-<name>` / `ai-tools-integration-<name>`): `agent-claude-code.rule.md`
for an agent, and the integration rules by their bare provider name. The generic seam
([providers](providers.rule.md)) and the generic launch contract ([launch](launch.rule.md)) hold
what is true of *every* provider; a provider rule holds only what is true of that one, and each
side links the other. A fact that would have to be restated for the second provider belongs in
the seam, not in a provider rule.
- **Avoid a stem that matches a secret pattern.** `ai-tools-chown` quarantines
secret-named files the agent writes (see [secret-handling](secret-handling.rule.md)): a
file whose basename matches `~/.config/ai-tools/secret-patterns` is chowned to
Expand Down
112 changes: 92 additions & 20 deletions .claude/rules/claude-settings.rule.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ paths:
`settings.json` is the agent session's Claude Code configuration. It declares the
ownership hooks (covered in [ownership-and-hooks](ownership-and-hooks.rule.md)), the
token-saving filter hook on both `Bash` events (covered in [filters](filters.rule.md)), the
Bash-tool permission rules, a privacy `env` block, and the auto-mode default. This rule
covers the **permission rules** and how they couple to the SELinux policy, the **`env`
privacy default**, and the **`disableAutoMode`** default. The catalog of other Claude Code
Bash-tool permission rules, an `env` block, the auto-mode default, and the observability
defaults. This rule covers the **permission rules** and how they couple to the SELinux policy,
the **`env` block**, the **observability defaults**, and the **`disableAutoMode`** default. The
catalog of other Claude Code
options an operator MAY add — and which are set elsewhere — is in
[`docs/claude-options.md`](../../docs/claude-options.md).

Expand Down Expand Up @@ -86,7 +87,32 @@ which is why the host-survey group below is denied rather than merely unlisted.

### Refused (`deny`)

Two groups with distinct criteria.
Three groups with distinct criteria.

**Irreversible VCS operations** — these **succeed**, and what they take has no undo: history
rewritten, a published branch overwritten for everyone else holding it, uncommitted or untracked
work deleted from the tree.

| Entry | What it destroys |
|---|---|
| `git push --force*` | The remote's history for every other clone. The pattern also covers `--force-with-lease`, which narrows the race but still overwrites. |
| `git push -f *` | The short spelling of the same. |
| `git reset --hard*` | The working tree and index, including changes never committed. |
| `git clean -f*` | Untracked files — the ones no commit and no reflog can bring back. |

The criterion is **destruction with no undo**, so the refusal holds regardless of target: a
scratch branch and `main` are denied alike, because a deny rule matches a command string and
cannot tell them apart. These would prompt if merely unlisted (they are mutations, not the
auto-approved safe reads of the host-survey group), and a prompt is the wrong gate for them —
it approves a command string, while what the operator has to weigh is what is about to be lost.
Denied, the agent raises the operation in conversation, and the operator runs it where the
consequence lands.

The group is deliberately narrow, and it is a gate rather than a boundary: the same destruction
is still reachable through a spelling the pattern does not match (`--force` placed after the
refspec, `git push origin +branch`, an `rm -rf` of the work tree), and matching those would take
a matcher over intent rather than over text. What it buys is that the **habitual** spellings —
the ones an agent reaches for without deliberating — cannot be taken silently.

**Categorical dead-ends** — the core posture refuses these regardless of arguments or
target, so a deny stops the agent spending a tool call, and emitting an AVC, on an
Expand Down Expand Up @@ -133,18 +159,40 @@ enforcement plus DAC (see [confinement](confinement.rule.md)); a `deny` entry on
the agent from attempting a denied action. Removing an entry re-exposes the attempt to the
SELinux floor — it does not by itself grant the capability.

`tests/integration/hooks.sh` pins both deny groups at install time (the verify phase runs
it): a missing categorical entry fails; host-survey relaxations are reported by name and
pass, but a file with none of them (a kept pre-upgrade settings.json) fails; an entry in
both lists fails as drift.

## `env` — the privacy default

The top-level `env` block applies environment variables to every session. It ships one
entry:
`tests/integration/hooks.sh` pins all three deny groups at install time (the verify phase
runs it): a missing categorical or irreversible-VCS entry fails; host-survey relaxations are
reported by name and pass, but a file with none of them (a kept pre-upgrade settings.json)
fails; an entry in both lists fails as drift. The irreversible-VCS entries are pinned
strictly rather than reported, because the paths that preserve a host's tuning — the
keep-existing install and `%config(noreplace)` on upgrade — are also the paths by which a
settings.json predating them, or edited in the permission arrays it invites tuning of,
silently loses the gate.

## The tool-call record is declared as its own matcher group

`post-tool-hook.sh` appears twice under `PostToolUse`: argument-less on `Write|Edit` (record
then hand back) and as `post-tool-hook.sh record` on `Bash` (record only). One widened
`Write|Edit|Bash` matcher would express the same intent in a single group and **would not reach
an upgraded host**: `ai_tools_conf_merge_hook_declarations` keys on the *command string*, not on
the matcher, so a kept `settings.json` already declaring that command counts the group as
present and the widened matcher is never merged in. The `Bash` records would then be emitted on
a fresh install and silently nowhere else — precisely the failure the merge exists to prevent.
A distinct argument makes it a distinct command string, so the merge carries it like any other
newly shipped declaration. This is the same dispatch-on-`$1` shape `session-hook.sh` and
`filter-hook.sh` already use, and it is why the argument-less form must stay argument-less:
renaming it would leave the old declaration in place beside the new one and run the handback
twice per write.

## `env` — the privacy and output defaults

The top-level `env` block applies environment variables to every session. It ships two
entries:

```json
"env": { "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1" }
"env": {
"CLAUDE_CODE_MAX_OUTPUT_TOKENS": 131072,
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
}
```

`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1` opts the session out of all non-essential
Expand All @@ -154,10 +202,33 @@ outbound traffic in one variable: it subsumes `DISABLE_TELEMETRY`,
individually. The essential Anthropic API traffic the agent needs is unaffected, as is the
WebFetch domain safety check (which has its own `skipWebFetchPreflight` opt-out, left on).

It lives here rather than in `ai-tools-run`'s allowlist because it is Claude Code product
`CLAUDE_CODE_MAX_OUTPUT_TOKENS=131072` sets the per-response output-token cap a session
requests. It shapes response length and cost, not authority — a capped and an uncapped session
may do exactly the same things.

Both live here rather than in `ai-tools-run`'s allowlist because they are Claude Code product
policy, not confinement structure — Claude Code's own config surface, beside the permission
and hook declarations. Layering and override are under "Control-plane integrity" below.

## `showThinkingSummaries` and `verbose` — the observability defaults

```json
"showThinkingSummaries": true,
"verbose": true
```

Both put more of a session in front of the operator watching it: `showThinkingSummaries` re-shows
the thinking blocks Claude Code hides by default, and `verbose` shows Bash and command output in
full rather than truncated. They cost terminal space and nothing else — the session's authority is
identical either way — and what they buy is that the operator confirming an action sees the
reasoning that produced it and the output it produced, which is the difference between approving a
command string and approving what the command did.

They are the operator-side complement to `disableAutoMode` below: that key decides *whether* a
human is asked, these decide *how much* that human is shown. The catalog of the other UI and
behavior keys an operator MAY add is in
[`docs/claude-options.md`](../../docs/claude-options.md).

## `disableAutoMode` — confirm-by-default

```json
Expand Down Expand Up @@ -201,11 +272,12 @@ in the agent-writable project tree. The layers compose differently per setting:
- The **deny rules** and **hook declarations** merge additively across every layer — a
deny from any source wins over any allow, and project hooks add to rather than replace
these — so a project layer cannot remove them. They hold for the whole session.
- The **`env` privacy default** and **`disableAutoMode`** are single-valued: a
higher-precedence project layer overrides them per key — control-plane defaults, not
locks. Neither is a containment boundary (telemetry is not one, and `disableAutoMode`
only removes confirmation prompts; the session's confinement is unchanged either way), so
a lock is unneeded. The one unoverridable layer, managed policy
- The **`env` block**, the **observability defaults**, and **`disableAutoMode`** are
single-valued: a higher-precedence project layer overrides them per key — control-plane
defaults, not locks. None is a containment boundary (telemetry and an output cap are not
one, the observability keys only change how much is displayed, and `disableAutoMode` only
removes confirmation prompts; the session's confinement is unchanged either way), so a lock
is unneeded. The one unoverridable layer, managed policy
(`/etc/claude-code/managed-settings.json`), is machine-wide — it applies to every Claude
Code user on the host — so the sandbox does not ship it.

Expand Down
Loading