@raishin/vanguard-frontier-agentic 3.15.0 → 3.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.cursor-plugin/plugin.json +1 -1
  4. package/.github/plugin/marketplace.json +1 -1
  5. package/catalog/AGENTS.md +2 -1
  6. package/catalog/asset-integrity.json +476 -29
  7. package/catalog/index.json +3 -2
  8. package/catalog/mods.json +131 -0
  9. package/mods/README.md +220 -0
  10. package/mods/audit-trail/README.md +70 -0
  11. package/mods/audit-trail/bin/hook.mjs +65 -0
  12. package/mods/audit-trail/claude-code/UNSUPPORTED.md +11 -0
  13. package/mods/audit-trail/claude-code/settings.hooks.json +14 -0
  14. package/mods/audit-trail/codex/hooks.json +14 -0
  15. package/mods/audit-trail/kiro/UNSUPPORTED.md +7 -0
  16. package/mods/audit-trail/kiro/audit-trail-post-tool-use-hook.json +14 -0
  17. package/mods/audit-trail/lib/common.mjs +213 -0
  18. package/mods/audit-trail/lib/core.mjs +50 -0
  19. package/mods/audit-trail/lib/runtime.mjs +140 -0
  20. package/mods/audit-trail/mod.toml +37 -0
  21. package/mods/blast-radius-guard/.claude-plugin/plugin.json +5 -0
  22. package/mods/blast-radius-guard/README.md +53 -0
  23. package/mods/blast-radius-guard/bin/hook.mjs +18 -0
  24. package/mods/blast-radius-guard/claude-code/settings.hooks.json +14 -0
  25. package/mods/blast-radius-guard/codex/UNSUPPORTED.md +7 -0
  26. package/mods/blast-radius-guard/codex/hooks.json +14 -0
  27. package/mods/blast-radius-guard/hooks/hooks.json +5 -0
  28. package/mods/blast-radius-guard/hooks/register.test.ts +97 -0
  29. package/mods/blast-radius-guard/hooks/register.ts +13 -0
  30. package/mods/blast-radius-guard/kiro/UNSUPPORTED.md +7 -0
  31. package/mods/blast-radius-guard/kiro/blast-radius-guard-pre-tool-use-hook.json +14 -0
  32. package/mods/blast-radius-guard/lib/common.mjs +213 -0
  33. package/mods/blast-radius-guard/lib/core.mjs +66 -0
  34. package/mods/blast-radius-guard/lib/patterns.mjs +102 -0
  35. package/mods/blast-radius-guard/lib/runtime.mjs +140 -0
  36. package/mods/blast-radius-guard/mod.toml +34 -0
  37. package/mods/blast-radius-guard/patterns.json +101 -0
  38. package/mods/context-burn/.claude-plugin/plugin.json +5 -0
  39. package/mods/context-burn/README.md +66 -0
  40. package/mods/context-burn/bin/hook.mjs +38 -0
  41. package/mods/context-burn/claude-code/UNSUPPORTED.md +7 -0
  42. package/mods/context-burn/claude-code/settings.hooks.json +24 -0
  43. package/mods/context-burn/codex/UNSUPPORTED.md +7 -0
  44. package/mods/context-burn/codex/hooks.json +24 -0
  45. package/mods/context-burn/hooks/hooks.json +5 -0
  46. package/mods/context-burn/hooks/register.test.ts +151 -0
  47. package/mods/context-burn/hooks/register.ts +35 -0
  48. package/mods/context-burn/kiro/UNSUPPORTED.md +7 -0
  49. package/mods/context-burn/kiro/context-burn-post-tool-use-hook.json +14 -0
  50. package/mods/context-burn/kiro/context-burn-session-start-hook.json +14 -0
  51. package/mods/context-burn/lib/common.mjs +213 -0
  52. package/mods/context-burn/lib/core.mjs +79 -0
  53. package/mods/context-burn/lib/runtime.mjs +140 -0
  54. package/mods/context-burn/mod.toml +37 -0
  55. package/mods/live-guard-gate/.claude-plugin/plugin.json +5 -0
  56. package/mods/live-guard-gate/README.md +43 -0
  57. package/mods/live-guard-gate/bin/hook.mjs +12 -0
  58. package/mods/live-guard-gate/claude-code/settings.hooks.json +14 -0
  59. package/mods/live-guard-gate/codex/UNSUPPORTED.md +7 -0
  60. package/mods/live-guard-gate/codex/hooks.json +14 -0
  61. package/mods/live-guard-gate/hooks/hooks.json +5 -0
  62. package/mods/live-guard-gate/hooks/register.test.ts +59 -0
  63. package/mods/live-guard-gate/hooks/register.ts +11 -0
  64. package/mods/live-guard-gate/kiro/UNSUPPORTED.md +7 -0
  65. package/mods/live-guard-gate/kiro/live-guard-gate-pre-tool-use-hook.json +14 -0
  66. package/mods/live-guard-gate/lib/common.mjs +213 -0
  67. package/mods/live-guard-gate/lib/core.mjs +24 -0
  68. package/mods/live-guard-gate/lib/live-agents.mjs +65 -0
  69. package/mods/live-guard-gate/lib/patterns.mjs +523 -0
  70. package/mods/live-guard-gate/lib/runtime.mjs +140 -0
  71. package/mods/live-guard-gate/mod.toml +34 -0
  72. package/mods/live-guard-gate/patterns.json +522 -0
  73. package/mods/secret-redactor/.claude-plugin/plugin.json +5 -0
  74. package/mods/secret-redactor/README.md +86 -0
  75. package/mods/secret-redactor/bin/hook.mjs +35 -0
  76. package/mods/secret-redactor/claude-code/UNSUPPORTED.md +7 -0
  77. package/mods/secret-redactor/claude-code/settings.hooks.json +24 -0
  78. package/mods/secret-redactor/codex/UNSUPPORTED.md +11 -0
  79. package/mods/secret-redactor/codex/hooks.json +14 -0
  80. package/mods/secret-redactor/hooks/hooks.json +5 -0
  81. package/mods/secret-redactor/hooks/register.test.ts +150 -0
  82. package/mods/secret-redactor/hooks/register.ts +19 -0
  83. package/mods/secret-redactor/kiro/UNSUPPORTED.md +11 -0
  84. package/mods/secret-redactor/kiro/secret-redactor-pre-tool-use-hook.json +14 -0
  85. package/mods/secret-redactor/lib/common.mjs +213 -0
  86. package/mods/secret-redactor/lib/core.mjs +61 -0
  87. package/mods/secret-redactor/lib/patterns.mjs +75 -0
  88. package/mods/secret-redactor/lib/runtime.mjs +140 -0
  89. package/mods/secret-redactor/mod.toml +43 -0
  90. package/mods/secret-redactor/patterns.json +74 -0
  91. package/package.json +8 -4
  92. package/plugins/vanguard-frontier-agentic/.codex-plugin/plugin.json +1 -1
  93. package/scripts/generate-mods.mjs +784 -0
  94. package/scripts/mods/common.mjs +213 -0
  95. package/scripts/mods/runtime.mjs +140 -0
  96. package/tests/fixtures/README.md +1 -1
  97. package/tests/fuzz-properties.test.mjs +254 -0
  98. package/tests/mods.test.mjs +768 -0
  99. package/tests/validate-asset-integrity.py +1 -0
  100. package/tests/validate-mods.py +821 -0
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "vanguard-frontier-agentic",
3
- "summary": "Curated cross-harness marketplace for cloud-focused agent skills, agents, rules, MCP references, and delegation workflows.",
3
+ "summary": "Curated cross-harness marketplace for cloud-focused agent skills, agents, rules, MCP references, delegation workflows, and mods.",
4
4
  "last_updated": "2026-05-09",
5
5
  "catalogs": {
6
6
  "skills": "catalog/skills.json",
7
7
  "agents": "catalog/agents.json",
8
8
  "rules": "catalog/rules.json",
9
9
  "mcp_references": "catalog/mcp-references.json",
10
- "workflows": "catalog/workflows.json"
10
+ "workflows": "catalog/workflows.json",
11
+ "mods": "catalog/mods.json"
11
12
  }
12
13
  }
@@ -0,0 +1,131 @@
1
+ [
2
+ {
3
+ "id": "audit-trail",
4
+ "name": "Audit Trail",
5
+ "version": "1.0.0",
6
+ "description": "Appends one JSON line per tool call to a per-session log with sha256 hashes of the arguments and result.",
7
+ "reach_level": 1,
8
+ "events": [
9
+ "PostToolUse"
10
+ ],
11
+ "harnesses": [
12
+ "claude-code",
13
+ "codex",
14
+ "kiro"
15
+ ],
16
+ "path": "mods/audit-trail",
17
+ "footprint": {
18
+ "env_reads": [
19
+ "VANGUARD_AUDIT_TRAIL_LOG_DIR",
20
+ "VANGUARD_AUDIT_TRAIL_SESSION_ID",
21
+ "VANGUARD_AUDIT_TRAIL_AGENT_ID"
22
+ ],
23
+ "file_writes": [
24
+ ".audit/**"
25
+ ],
26
+ "spawns": [],
27
+ "egress_hosts": []
28
+ }
29
+ },
30
+ {
31
+ "id": "blast-radius-guard",
32
+ "name": "Blast Radius Guard",
33
+ "version": "1.0.0",
34
+ "description": "Asks before, or blocks, destructive shell commands and writes to system paths.",
35
+ "reach_level": 0,
36
+ "events": [
37
+ "PreToolUse"
38
+ ],
39
+ "harnesses": [
40
+ "claude-code",
41
+ "codex",
42
+ "kiro"
43
+ ],
44
+ "path": "mods/blast-radius-guard",
45
+ "footprint": {
46
+ "env_reads": [
47
+ "VANGUARD_BLAST_RADIUS_GUARD_MODE",
48
+ "VANGUARD_BLAST_RADIUS_GUARD_PATTERNS"
49
+ ],
50
+ "file_writes": [],
51
+ "spawns": [],
52
+ "egress_hosts": []
53
+ }
54
+ },
55
+ {
56
+ "id": "context-burn",
57
+ "name": "Context Burn",
58
+ "version": "1.0.0",
59
+ "description": "Tracks context use per session in a status file and warns at 70% and 90% where the harness reports token use.",
60
+ "reach_level": 1,
61
+ "events": [
62
+ "PostToolUse",
63
+ "SessionStart"
64
+ ],
65
+ "harnesses": [
66
+ "claude-code",
67
+ "codex",
68
+ "kiro"
69
+ ],
70
+ "path": "mods/context-burn",
71
+ "footprint": {
72
+ "env_reads": [
73
+ "VANGUARD_CONTEXT_BURN_STATUS_PATH"
74
+ ],
75
+ "file_writes": [
76
+ ".claude/context-burn-status/*.json",
77
+ ".kiro/context-burn-status/*.json",
78
+ ".codex/context-burn-status/*.json"
79
+ ],
80
+ "spawns": [],
81
+ "egress_hosts": []
82
+ }
83
+ },
84
+ {
85
+ "id": "live-guard-gate",
86
+ "name": "Live Guard Gate",
87
+ "version": "1.0.0",
88
+ "description": "Asks before live cloud mutations and names the guard agent that should review them.",
89
+ "reach_level": 0,
90
+ "events": [
91
+ "PreToolUse"
92
+ ],
93
+ "harnesses": [
94
+ "claude-code",
95
+ "codex",
96
+ "kiro"
97
+ ],
98
+ "path": "mods/live-guard-gate",
99
+ "footprint": {
100
+ "env_reads": [],
101
+ "file_writes": [],
102
+ "spawns": [],
103
+ "egress_hosts": []
104
+ }
105
+ },
106
+ {
107
+ "id": "secret-redactor",
108
+ "name": "Secret Redactor",
109
+ "version": "1.0.0",
110
+ "description": "Asks before tool calls whose input carries a credential and redacts credentials from tool output.",
111
+ "reach_level": 0,
112
+ "events": [
113
+ "PreToolUse",
114
+ "PostToolUse"
115
+ ],
116
+ "harnesses": [
117
+ "claude-code",
118
+ "codex",
119
+ "kiro"
120
+ ],
121
+ "path": "mods/secret-redactor",
122
+ "footprint": {
123
+ "env_reads": [
124
+ "VANGUARD_SECRET_REDACTOR_PATTERNS"
125
+ ],
126
+ "file_writes": [],
127
+ "spawns": [],
128
+ "egress_hosts": []
129
+ }
130
+ }
131
+ ]
package/mods/README.md ADDED
@@ -0,0 +1,220 @@
1
+ # Vanguard Mods
2
+
3
+ <div align="center">
4
+
5
+ <img src="../assets/logos/vanguard-frontier-agentic-logo.png" alt="Vanguard Frontier Agentic" width="140" />
6
+
7
+ <p><strong>Less “oops.” More visibility.</strong></p>
8
+ <p>Tool-call guardrails and observers for Claude Code, Kiro and Codex.</p>
9
+
10
+ <p>
11
+ <a href="#pick-a-mod">Pick a mod</a> &nbsp;·&nbsp;
12
+ <a href="#get-started">Get started</a> &nbsp;·&nbsp;
13
+ <a href="#know-the-limits">Know the limits</a> &nbsp;·&nbsp;
14
+ <a href="#under-the-hood">Under the hood</a>
15
+ </p>
16
+
17
+ </div>
18
+
19
+ ---
20
+
21
+ Skills guide the agent. **Mods inspect its tool calls.** Ask before risky actions,
22
+ redact supported output, or record what happened. Pick what you need—not everything at once.
23
+
24
+ <a id="pick-a-mod"></a>
25
+
26
+ ## 🧰 Pick your guardrails
27
+
28
+ | Mod | The job | Reach |
29
+ | --- | --- | --- |
30
+ | [Secret Redactor](secret-redactor/README.md) | Spot credential-shaped input; redact supported output. | L0 |
31
+ | [Blast Radius Guard](blast-radius-guard/README.md) | Ask or deny on destructive commands and system-path writes. | L0 |
32
+ | [Live Guard Gate](live-guard-gate/README.md) | Flag cloud mutations and name guard agents to consult. | L0 |
33
+ | [Context Burn](context-burn/README.md) | Count tool calls; warn on reported context use where supported. | L1 |
34
+ | [Audit Trail](audit-trail/README.md) | Append call hashes and per-session sequence numbers. | L1 |
35
+
36
+ **Reach is a label, not a sandbox:** L0 = no declared writes/spawns/network;
37
+ L1 = file writes; L2 = subprocesses; L3 = network (warned, not blocked).
38
+ Reads are allowed at L0; starting the Node adapter is not a mod-spawned subprocess.
39
+
40
+ **Support differs:** Kiro/Codex deny instead of asking and cannot redact output.
41
+ Claude Code Audit Trail needs classic. Classic Post hooks miss failed calls.
42
+
43
+ <details>
44
+ <summary><strong>🔎 Same mod, different harness? Check the fine print.</strong></summary>
45
+
46
+ | Mod | Claude Code native | Claude Code classic | Kiro / Codex |
47
+ | --- | --- | --- | --- |
48
+ | Secret Redactor | Ask on input; redact or withhold output | Ask on input; replace successful output only | Deny matching input; no output redaction |
49
+ | Blast Radius Guard | Ask in confirm mode; deny in block mode | Same | Deny in either mode |
50
+ | Live Guard Gate | Ask and name guard agents | Same | Deny and name guard agents |
51
+ | Context Burn | Session context usage and threshold toasts | Tool-call counter only | Tool-call counter only |
52
+ | Audit Trail | Unsupported—use classic | Append after successful calls | Append; Kiro payload interpretation is degraded |
53
+
54
+ Guards use `PreToolUse`; supported redaction and observers use `PostToolUse`;
55
+ Context Burn also uses `SessionStart`.
56
+ Native equivalents are `tool.check`, `tool.call` and `session.start`.
57
+
58
+ Kiro's stdin fields are undocumented: guards scan the whole payload if input is
59
+ unrecognized; Audit Trail hashes that payload as arguments and `null` as the result.
60
+ Set `VANGUARD_AUDIT_TRAIL_SESSION_ID` to avoid its shared `unsessioned` fallback.
61
+ See generated `<harness>/UNSUPPORTED.md` for remaining limits.
62
+
63
+ </details>
64
+
65
+ ---
66
+
67
+ <a id="get-started"></a>
68
+
69
+ ## 🚀 Get started
70
+
71
+ Use a local checkout. Command adapters need **Node 22+**; native modules use the
72
+ harness runtime. No service needed.
73
+
74
+ ```bash
75
+ source=/absolute/path/to/vanguard-frontier-agentic
76
+ target=/absolute/path/to/your/project
77
+ mod=blast-radius-guard
78
+ ```
79
+
80
+ > [!IMPORTANT]
81
+ > **Native or classic. Not both for the same mod.** They duplicate checks and can
82
+ > double-count Context Burn. Audit Trail has no native adapter—use classic.
83
+
84
+ <details>
85
+ <summary><strong>🤖 Claude Code native</strong> — load a plugin root</summary>
86
+
87
+ ```bash
88
+ cd "$target"
89
+ claude --plugin-dir "$source/mods/$mod"
90
+ ```
91
+
92
+ Not for `audit-trail`. Native API: early access. Check the verified build in the
93
+ [implementation notes](#under-the-hood) before upgrading.
94
+
95
+ </details>
96
+
97
+ <details>
98
+ <summary><strong>🤖 Claude Code classic</strong> — copy, then merge hooks</summary>
99
+
100
+ ```bash
101
+ mkdir -p "$target/.claude/vanguard-mods/$mod"
102
+ cp -R "$source/mods/$mod/bin" "$source/mods/$mod/lib" "$target/.claude/vanguard-mods/$mod/"
103
+ ```
104
+
105
+ Append `mods/<id>/claude-code/settings.hooks.json` entries under `hooks` in the
106
+ target's `.claude/settings.json`. **Keep existing hooks/settings.** Generated command:
107
+
108
+ ```text
109
+ node "$CLAUDE_PROJECT_DIR/.claude/vanguard-mods/<id>/bin/hook.mjs" --harness claude-code --event <event>
110
+ ```
111
+
112
+ `CLAUDE_PROJECT_DIR` stays at the initial project root, even across worktree changes.
113
+ Observers resolve relative writes from the installed adapter location.
114
+
115
+ </details>
116
+
117
+ <details>
118
+ <summary><strong>🔮 Kiro</strong> — copy the adapters and hook JSON</summary>
119
+
120
+ ```bash
121
+ mkdir -p "$target/.kiro/hooks" "$target/.kiro/vanguard-mods/$mod"
122
+ cp -R "$source/mods/$mod/bin" "$source/mods/$mod/lib" "$target/.kiro/vanguard-mods/$mod/"
123
+ cp "$source/mods/$mod/kiro/"*-hook.json "$target/.kiro/hooks/"
124
+ ```
125
+
126
+ Copy only `*-hook.json`, not `UNSUPPORTED.md`. Replace the relative script path
127
+ inside each hook with its **absolute installed path**: hook cwd is undocumented.
128
+ Commands use `--harness kiro --event <event>`; confirmation requests deny with exit 2.
129
+
130
+ </details>
131
+
132
+ <details>
133
+ <summary><strong>⚡ Codex</strong> — copy, merge, then review trust</summary>
134
+
135
+ ```bash
136
+ mkdir -p "$target/.codex/vanguard-mods/$mod"
137
+ cp -R "$source/mods/$mod/bin" "$source/mods/$mod/lib" "$target/.codex/vanguard-mods/$mod/"
138
+ ```
139
+
140
+ Append event entries from `mods/<id>/codex/hooks.json` to the target's
141
+ `.codex/hooks.json`, preserving existing hooks. Replace relative script paths with
142
+ **absolute installed paths**; hook cwd is undocumented. Complete Codex's hook trust review.
143
+
144
+ Commands use `--harness codex --event <event>`; asks deny with exit 2.
145
+ Redactor Post is unsupported; the narrower MCP output field is not substituted.
146
+
147
+ </details>
148
+
149
+ **Try it before trusting it:** run ordinary and harmless pattern-matching calls in
150
+ the harness. Inspect the decision, output or observer file—not just exit codes.
151
+ Repeat after path, permission, Node or harness changes.
152
+
153
+ ---
154
+
155
+ <a id="know-the-limits"></a>
156
+
157
+ ## ⚠️ Guardrails, not guarantees
158
+
159
+ - **A hook must run to help.** Guards deny handler/input errors; missing Node,
160
+ moved scripts, startup crashes and timeouts can leave calls unguarded.
161
+ CLI errors may not block; Kiro IDE handling differs.
162
+ - **Regex is not complete DLP.** Pre checks never rewrite arguments; approved input
163
+ still reaches the tool. Post redaction does not scrub other logs or network traffic.
164
+ Redactor errors attempt withholding; classic shape mismatches may be ignored.
165
+ [Withholding has limits, too](secret-redactor/README.md#limitations).
166
+ - **A review prompt is not authorization.** Live Guard Gate names agents—it never
167
+ spawns them or verifies production accounts. Guards scan strings, not shell semantics;
168
+ relative paths stay unresolved. Bypass modes can auto-approve an ask.
169
+ - **Observers are not compliance proof.** Node counters track calls, not tokens or
170
+ spend; only native Context Burn uses reported context data. Unlocked updates can
171
+ lose increments. Audit logs are mutable, unsigned and not tamper-evident.
172
+ Observer failures do not block tools.
173
+
174
+ <details>
175
+ <summary><strong>🗂️ State files and custom patterns</strong></summary>
176
+
177
+ - **Context Burn:** `.<harness>/context-burn-status/<session-hash>.json`
178
+ (`.claude` for Claude Code). Filenames use the full lowercase SHA-256 of the UTF-8
179
+ session ID, never raw IDs. Missing/empty Node IDs share `unsessioned` state.
180
+ `VANGUARD_CONTEXT_BURN_STATUS_PATH` is an exact override; sharing it opts out of
181
+ isolation. Old single-file state is not migrated automatically.
182
+ - **Audit Trail:** `.audit/<UTC-date>/<session>.jsonl`; an earlier file is reused
183
+ across dates. `VANGUARD_AUDIT_TRAIL_LOG_DIR` overrides the directory.
184
+ - **Footprints disclose defaults, not containment.** Overrides can write outside
185
+ declared globs; overlap checks do not constrain operator overrides.
186
+ - **Patterns append, never replace.** `VANGUARD_SECRET_REDACTOR_PATTERNS` and
187
+ `VANGUARD_BLAST_RADIUS_GUARD_PATTERNS` accept extra pattern files. Use absolute paths.
188
+ Unreadable/invalid configured files deny guards or withhold redactor output.
189
+ Each mod README documents schemas and environment names.
190
+
191
+ </details>
192
+
193
+ <a id="under-the-hood"></a>
194
+
195
+ ## 🔧 Under the hood
196
+
197
+ `mod.toml` declares support and footprint; `lib/core.mjs` holds shared behavior.
198
+ Edit those sources, `patterns.json`, entries and native modules—not generated adapters.
199
+ The generator produces manifests, shared runtime copies, pattern modules and `catalog/mods.json`.
200
+
201
+ <details>
202
+ <summary><strong>🛠️ Contributing? Run the gates.</strong></summary>
203
+
204
+ ```bash
205
+ npm run mods:write
206
+ npm run validate:mods
207
+ npm run test:fuzz
208
+ npm run asset-integrity:write # last generator, on its own
209
+ npm run validate
210
+ ```
211
+
212
+ The Python gate needs **Python 3.11+**. For each supported native mod, also run
213
+ `claude plugin validate mods/<id>` and `claude plugin test mods/<id>` locally;
214
+ CI has no native CLI. Spelling and Markdown lint are separate gates.
215
+ Static checks are not proof that an installed hook runs.
216
+
217
+ </details>
218
+
219
+ [Implementation plan](https://github.com/VincentChuWaiChow/vanguard-frontier-agentic/blob/dc8b29c823fb89dc4246413f38a7b82fdd34aa18/.claude/plan/cross-harness-mods.md)
220
+ · [Spec deviations](https://github.com/VincentChuWaiChow/vanguard-frontier-agentic/blob/dc8b29c823fb89dc4246413f38a7b82fdd34aa18/.kiro/specs/cross-harness-mods/deviations.md)
@@ -0,0 +1,70 @@
1
+ # Audit Trail
2
+
3
+ Appends one JSON line per tool call to a per-session log. It is an honest append log: it makes
4
+ no claim of immutability, carries no signatures, and any process with write access can edit or
5
+ delete the file.
6
+
7
+ ## Reach and declared footprint
8
+
9
+ **L1 (file writes).** The manifest declares default writes to `.audit/**`, reads
10
+ `VANGUARD_AUDIT_TRAIL_LOG_DIR`, `VANGUARD_AUDIT_TRAIL_SESSION_ID` and
11
+ `VANGUARD_AUDIT_TRAIL_AGENT_ID`, and declares no subprocess spawns or network egress.
12
+ This is capability disclosure, not isolation; the log-directory override can write
13
+ outside the declared default footprint.
14
+
15
+ ## What it records
16
+
17
+ | Field | Value |
18
+ |---|---|
19
+ | `timestamp` | UTC ISO 8601 time of the hook run |
20
+ | `session_id` | The safe session id (see below) |
21
+ | `agent_id` | `VANGUARD_AUDIT_TRAIL_AGENT_ID`, or `null` |
22
+ | `tool_name` | The payload `tool_name`, or `null` |
23
+ | `args_hash` | SHA-256 of the canonical JSON of `tool_input` (`null` when absent) |
24
+ | `result_hash` | SHA-256 of the canonical JSON of `tool_response` (`null` when absent) |
25
+ | `sequence_number` | 1, 2, 3 ... per session file |
26
+
27
+ Only hashes are stored, never the arguments or results themselves.
28
+
29
+ ## File layout
30
+
31
+ `<dir>/<YYYY-MM-DD>/<session>.jsonl`, where `<dir>` defaults to `.audit` and dates are UTC. If
32
+ the session already has a file under an earlier date directory, that file is reused, so the
33
+ sequence does not reset at midnight. A relative `<dir>` resolves against the project root when
34
+ the hook runs from its installed location, otherwise against the working directory.
35
+
36
+ ## Session ids
37
+
38
+ 1. `VANGUARD_AUDIT_TRAIL_SESSION_ID`, when set and non-empty.
39
+ 2. The payload `session_id`, when a non-empty string.
40
+ 3. Otherwise `unsessioned`.
41
+
42
+ An id that does not match `[A-Za-z0-9._-]{1,128}` is replaced by the first 32 hex characters of
43
+ its SHA-256, so it can never escape the date directory.
44
+
45
+ ## Environment
46
+
47
+ | Variable | Purpose |
48
+ |---|---|
49
+ | `VANGUARD_AUDIT_TRAIL_LOG_DIR` | Log directory (default `.audit`). It can point outside the declared `.audit/**` write footprint; that is the operator's choice. |
50
+ | `VANGUARD_AUDIT_TRAIL_SESSION_ID` | Session id override |
51
+ | `VANGUARD_AUDIT_TRAIL_AGENT_ID` | Value for `agent_id` |
52
+
53
+ ## Harness support
54
+
55
+ | Harness | Status |
56
+ |---|---|
57
+ | Claude Code native | Unsupported: the native API has no file append. Install the classic hook instead. |
58
+ | Claude Code classic | Supported. `PostToolUse` fires only after a tool succeeds, so failed calls are not logged. |
59
+ | Kiro | Degraded: stdin field names are undocumented, so `args_hash` covers the whole payload, `result_hash` hashes `null`, and without a session id env var every session shares the `unsessioned` log. |
60
+ | Codex | Supported. |
61
+
62
+ ## Locking and failure
63
+
64
+ Each append takes an exclusive `<dir>/<session>.lock` before selecting the session file
65
+ (retrying for up to 2 s). Locks are never automatically stolen or removed based on age.
66
+ After a crash, confirm no writer remains before manually removing its orphaned lock.
67
+ A corrupt or unterminated last line, a lock timeout, or any I/O error exits 1 with a message on stderr and
68
+ appends nothing. A failure is loud but never blocks the tool call.
69
+
70
+ Requires Node 22+. Install: see `mods/README.md`.
@@ -0,0 +1,65 @@
1
+ // Node hook for audit-trail: appends one JSON line per tool call to a per-session log.
2
+ // An honest append log, not tamper-evident: anything with write access can edit it.
3
+ import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, unlinkSync } from 'node:fs'
4
+ import { dirname, join } from 'node:path'
5
+ import { runHook, projectPath } from '../lib/runtime.mjs'
6
+ import { buildRecord, lastSequence, rawSessionId, safeSessionId } from '../lib/core.mjs'
7
+
8
+ const sleep = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms)
9
+
10
+ // Reuses the earliest date dir that already holds this session, so the sequence never
11
+ // resets at midnight.
12
+ // ponytail: scans every date dir, O(days); index it if slow.
13
+ function logFile(base, sid, today) {
14
+ const days = existsSync(base) ? readdirSync(base).filter((d) => /^\d{4}-\d{2}-\d{2}$/.test(d)).sort() : []
15
+ const hit = days.find((d) => existsSync(join(base, d, `${sid}.jsonl`)))
16
+ return join(base, hit ?? today, `${sid}.jsonl`)
17
+ }
18
+
19
+ // Never steal a lock: its age does not prove the owner is dead. Explicit recovery is safer
20
+ // than racing two writers after an automatic stale-lock takeover.
21
+ function acquire(lock, file) {
22
+ const deadline = Date.now() + 2000
23
+ for (;;) {
24
+ try {
25
+ return openSync(lock, 'wx')
26
+ } catch (err) {
27
+ if (err.code !== 'EEXIST') throw err
28
+ }
29
+ if (Date.now() > deadline) throw new Error(`audit-trail: could not lock ${file} within 2s`)
30
+ sleep(20)
31
+ }
32
+ }
33
+
34
+ // Read directly instead of exists-then-read, so there is no check/use gap.
35
+ function readIfExists(file) {
36
+ try {
37
+ return readFileSync(file, 'utf8')
38
+ } catch (err) {
39
+ if (err.code === 'ENOENT') return ''
40
+ throw err
41
+ }
42
+ }
43
+
44
+ async function PostToolUse(payload, ctx) {
45
+ const base = projectPath(process.env.VANGUARD_AUDIT_TRAIL_LOG_DIR || '.audit', ctx.harness, import.meta.dirname)
46
+ const sid = await safeSessionId(rawSessionId(process.env.VANGUARD_AUDIT_TRAIL_SESSION_ID, payload))
47
+ const timestamp = new Date().toISOString()
48
+ mkdirSync(base, { recursive: true })
49
+ // Serialize session-file selection as well as appends, including across midnight.
50
+ const lock = join(base, `${sid}.lock`)
51
+ const fd = acquire(lock, lock)
52
+ try {
53
+ const file = logFile(base, sid, timestamp.slice(0, 10))
54
+ mkdirSync(dirname(file), { recursive: true })
55
+ const sequence = lastSequence(readIfExists(file), file) + 1
56
+ const agentId = process.env.VANGUARD_AUDIT_TRAIL_AGENT_ID
57
+ const record = await buildRecord({ timestamp, sessionId: sid, agentId, payload, harness: ctx.harness, sequence })
58
+ appendFileSync(file, JSON.stringify(record) + '\n')
59
+ } finally {
60
+ closeSync(fd)
61
+ unlinkSync(lock)
62
+ }
63
+ }
64
+
65
+ process.exitCode = await runHook({ name: 'audit-trail', handlers: { PostToolUse } })
@@ -0,0 +1,11 @@
1
+ # Audit Trail on Claude Code
2
+
3
+ <!-- Generated by scripts/generate-mods.mjs from mod.toml. Do not edit. -->
4
+
5
+ ## Unsupported
6
+
7
+ - `native`: The native mod API has no file append; rewriting the whole log would break append-only writes. Install the classic hook instead.
8
+
9
+ ## Degraded
10
+
11
+ - `classic`: Classic PostToolUse fires only after a tool succeeds, so failed tool calls are not logged.
@@ -0,0 +1,14 @@
1
+ {
2
+ "hooks": {
3
+ "PostToolUse": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/vanguard-mods/audit-trail/bin/hook.mjs\" --harness claude-code --event PostToolUse"
9
+ }
10
+ ]
11
+ }
12
+ ]
13
+ }
14
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "hooks": {
3
+ "PostToolUse": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "node .codex/vanguard-mods/audit-trail/bin/hook.mjs --harness codex --event PostToolUse"
9
+ }
10
+ ]
11
+ }
12
+ ]
13
+ }
14
+ }
@@ -0,0 +1,7 @@
1
+ # Audit Trail on Kiro
2
+
3
+ <!-- Generated by scripts/generate-mods.mjs from mod.toml. Do not edit. -->
4
+
5
+ ## Degraded
6
+
7
+ - `PostToolUse`: Kiro does not document stdin field names; args_hash covers the whole payload, result_hash hashes null, and without VANGUARD_AUDIT_TRAIL_SESSION_ID every session shares the unsessioned log.
@@ -0,0 +1,14 @@
1
+ {
2
+ "version": "v1",
3
+ "hooks": [
4
+ {
5
+ "name": "audit-trail:PostToolUse",
6
+ "description": "Appends one JSON line per tool call to a per-session log with sha256 hashes of the arguments and result.",
7
+ "trigger": "PostToolUse",
8
+ "action": {
9
+ "type": "command",
10
+ "command": "node .kiro/vanguard-mods/audit-trail/bin/hook.mjs --harness kiro --event PostToolUse"
11
+ }
12
+ }
13
+ ]
14
+ }