Skip to content

docs(readme): explain the reasoning behind three design choices - #260

Merged
killertcell428 merged 2 commits into
masterfrom
docs/readme-answer-the-why
Aug 24, 2026
Merged

docs(readme): explain the reasoning behind three design choices#260
killertcell428 merged 2 commits into
masterfrom
docs/readme-answer-the-why

Conversation

@killertcell428

@killertcell428 killertcell428 commented Aug 24, 2026

Copy link
Copy Markdown
Owner

Why

Three claims in the README stated a fact but not the reasoning behind it. Left that way, each one reads as either naive or arbitrary to the audience the README is written for.

Each addition states its conclusion up front and then explains why it holds — no posed questions, no "you might be wondering".

1. HMAC signing — the key's location bounds what it proves

The biggest gap. The key defaults to .aigis/audit_key on the same machine as the agent (signed_log.py:114), auto-generated. The signature therefore catches an outside editor, not the log's own author — the developer running the agent has the key.

The README claimed tamper-evidence without qualifying it. Adds a section naming the boundary and the three mitigations, ordered by what they cover:

  • explicit key held off-machine (CI-injected / secrets manager)
  • ACLs on the key file, including the Windows caveat since chmod isn't enforced there (signed_log.py:152)
  • SIEM forwarding — the control that holds when the person being audited is the person on the machine

2. Deny-list default — the reason it's the default

Previously stated the fact and the fail-closed recipe but not why deny-list is the shipped default. Adds it: an agent that can't run ls isn't usable. Also notes that enumerating allow rules for a fail-closed posture is real work, so it can be budgeted rather than discovered later.

3. Near-binary capability axes — judgement is a team-level decision

shell: none | unrestricted with no allowlist invites the assumption that an allowlist was simply overlooked. Adds the reasoning: a prompt only protects someone in a position to judge it, and a non-engineer interrupted mid-task either approves everything or refuses everything.

Also documents the constraint behind git having three values rather than an on/off switch: capability rules are evaluated before the baseline, so any axis emitting a blanket git push* allow would sit in front of the baseline's *--force* deny and silently re-enable force-push.

Verification

Every claim checked against code, not docs:

Claim Source
Key path .aigis/audit_key, auto-generated signed_log.py:114-157
Windows needs manual NTFS ACLs signed_log.py:152
default_decision is allow policy.py:72
git values are none/local/push profiles.py:59
git is the only allow-emitting axis tests/test_profiles.py:91

Test plan

  • Anchor targets match their in-page links (#the-keys-location-bounds-what-the-signature-proves, #鍵の置き場所が署名で証明できる範囲を決める)
  • tests/test_profiles.py link resolves
  • Docs-only change

Three places where a security reviewer's obvious follow-up question went
unanswered, so the claim read as naive or arbitrary:

1. "Each record is HMAC-signed" — but the key defaults to
   .aigis/audit_key on the same machine as the agent, so the signature
   does not prove the developer running the agent didn't edit the log.
   The README never said this. Adds a section stating the boundary and
   the three actual mitigations (explicit key held off-machine, ACLs on
   the key file, SIEM forwarding — the one that holds for insider
   threat).

2. "The shipped rules are a deny-list" — stated the fact and the
   fail-closed recipe but never why deny-list is the default. Adds the
   reason (an agent that can't run `ls` is not usable) and notes that
   enumerating allow rules is real work.

3. The six capability axes are near-binary with no "ask the user"
   option, which invites "why not an allowlist?" Adds the reasoning
   (a prompt only protects someone who can judge it) and the
   non-obvious constraint behind git having three values (capability
   allows are evaluated before the baseline, so a blanket `git push*`
   allow would re-enable force-push).

Every claim verified against the code: signed_log.py:114-157 for the key
path and the Windows ACL caveat, policy.py:72 for default_decision,
profiles.py:59 for the git values, tests/test_profiles.py:91 for the
only-git-emits-allow assertion.

Signed-off-by: killertcell428 <killertcell428@gmail.com>
The previous version of this branch introduced question-form labels
("Why there is no ask-the-user setting", "What the signed log does not
prove"), which raise the doubt before answering it. Reversed: each label
now states the conclusion, and the body explains why it holds.

  Why there is no "ask the user" setting
    -> Judgement happens here, once, for the whole group

  What the signed log does not prove
    -> The key's location bounds what the signature proves

Also drops the "that is the right question to ask" framing in the audit
section and replaces it with a plain ordering of the three mitigations by
what they cover. Anchor links updated to match; the Japanese heading
dropped its comma so the generated anchor is unambiguous.

Signed-off-by: killertcell428 <killertcell428@gmail.com>
@killertcell428 killertcell428 changed the title docs(readme): answer the "why this, and is that enough?" questions docs(readme): explain the reasoning behind three design choices Aug 24, 2026
@killertcell428
killertcell428 merged commit 859c2c7 into master Aug 24, 2026
11 checks passed
@killertcell428
killertcell428 deleted the docs/readme-answer-the-why branch August 24, 2026 11:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant