Przejdź do głównej zawartości

Governance hooków — repo + bootstrap + signing

Pytanie 18 (Wzmacnianie organizacji): Jak zarządzasz hookami w skali firmy (Stop, PreToolUse, PostToolUse)?

Odpowiedź na max: Repo + auto-install przez dotfiles bootstrap + podpisane/audytowane skrypty hooków.

Dlaczego to ważne: Hooki uruchamiają kod przy każdym wywołaniu narzędzia. Niezaudytowany drift hooków = ryzyko supply chain. Ale dobrze zrobione współdzielone hooki to 10× mnożnik produktywności.

Dlaczego to ważne w 2026 (ryzyko supply chain vs 10× mnożnik)

Dział zatytułowany „Dlaczego to ważne w 2026 (ryzyko supply chain vs 10× mnożnik)”

Hooki to najpotężniejsza — i najniebezpieczniejsza — powierzchnia rozszerzeń w Claude Code, Codex i Cursor. Hook PreToolUse uruchamia się przed każdym wywołaniem narzędzia: każdym bash, każdym zapisem pliku, każdym requestem MCP. Hook PostToolUse po. Hook Stop po tym, jak agent mówi że skończył. Hooki widzą wszystko, mogą to mutować lub blokować i wykonują się jako zwykły proces na maszynie developera, z jego credentialami, kluczami SSH i tokenami cloudowymi.

Ta moc działa w dwie strony. Z plusem — hooki zamieniają probabilistycznego agenta w system z governance. Hook PreToolUse greppujący rm -rf / blokuje go deterministycznie — regex się nie jailbreakuje. Hook PostToolUse uruchamiający prettier --write po każdej edycji daje zespołowi dyscyplinę formatowania za darmo. Hook Stop automatycznie otwierający PR skraca pętlę “napisz kod → wypchnij kod” z minut do sekund. Zespoły dobrze wykorzystujące hooki w 2026 raportują 5–10× szybszy cycle time na rutynowej robocie, bo agenci nie potrzebują człowieka do oczywistych kroków po wywołaniu narzędzia.

Z minusem — hooki to podręcznikowy cel supply chain. Fala ataków na npm i template’y GitHub w kwietniu 2026 — Shai-Hulud, TeamPCP, kompromitacja SAP-CAP — zawierała payloady, które wstrzykiwały pliki .claude/settings.json ze złośliwymi hookami SessionStart do open-source’owych repo, tak że samo otwarcie repo w Claude Code lub VS Code triggerowało eksfiltrację credentiali z dotfiles developera. Pojedynczy niezaudytowany skrypt hooka we współdzielonym repo to, w najgorszym przypadku, root na każdej maszynie developera w organizacji.

Odpowiedź na max dla Q18 (“repo + auto-install przez dotfiles bootstrap + podpisane/audytowane skrypty hooków”) zamyka obie luki naraz. Pojedyncze repo git jest właścicielem każdego współdzielonego hooka. Dotfiles bootstrap instaluje je idempotentnie. Skrypty są podpisane, więc zmodyfikowany hook nie uruchomi się niezauważony. Każde wykonanie jest logowane, więc audyt i incident response faktycznie działają. Dostajesz 10× upside bez ryzyka supply chain.

Jak wygląda “max score” (repo + auto-install + signing + audyt)

Dział zatytułowany „Jak wygląda “max score” (repo + auto-install + signing + audyt)”

Cztery właściwości, każda nienegocjowalna:

1. Firmowe repo hooków. Pojedyncze repozytorium git (acme/agent-hooks lub zmergowane ze skills jako acme/agent-platform) zawiera każdy współdzielony hook. Jeden folder na hook, z hook.json deklarującym event cyklu życia, matcher narzędzia, ownera i timeout. Review przez PR, ma CODEOWNERS, ide przez ten sam release process co kod produkcyjny.

2. Auto-install przez dotfiles bootstrap. Współdzielone repo dotfiles klonuje repo hooków do znanej lokalizacji i wpisuje odpowiednie wpisy do ~/.claude/settings.json, ~/.cursor/settings.json i ~/.codex/config.toml. Bootstrap jest idempotentny, uruchamia się w onboardingu day-1 i re-uruchamia przy każdym logowaniu do shella, więc konfiguracja się samonaprawia. Developerzy nigdy nie edytują skryptów hooków w ~/.claude/hooks/ bezpośrednio — edytują w repo i wystawiają PR.

3. Podpisane skrypty hooków. Każdy hook ma weryfikowalny podpis. Dwa wzorce: (a) podpisane commity git na branch-protected main, z bootstrap odmawiającym instalacji, jeśli git verify-commit zawiedzie; (b) sigstore / cosign attached signatures, z pre-execution wrapperem wywołującym cosign verify-blob przed exec’iem hooka. (b) jest cięższe, ale pozwala dystrybuować poza gitem bez utraty łańcucha.

4. Audytowalne wykonanie. Każdy hook pisze ustrukturyzowaną linię JSON — timestamp, nazwa hooka, event, tool, command, exit code, decyzja — do ~/.acme-hooks/audit.log i (opcjonalnie) do SIEM. Czytane przez wewnętrzny dashboard, review’owane kwartalnie. Gdy coś się psuje, log jest źródłem prawdy.

Maszyna nowego hire po bootstrapie:

Okno terminala
$ ~/.acme-dotfiles/bootstrap.sh
[ok] Sklonowano acme/agent-hooks do ~/.acme-agent-hooks
[ok] Zweryfikowano podpisane commity na origin/main
[ok] Zainstalowano 12 hooków do ~/.claude/settings.json
$ claude
> /hooks
PreToolUse: deny-dangerous-commands, redact-secrets, cost-cap
PostToolUse: auto-format, tag-ai-pr, run-typecheck
Stop: open-pr, sync-skills, notify-slack
SessionStart: skills-sync, hooks-sync, audit-rotate

Bez “spytaj na Slacku, które hooki zainstalować.” Bez driftu. Bez cichego lądowania złośliwego hooka w ~/.claude/hooks/.

Konwencja 2026, ustalona przez opis Agent Governance od Endor Labs i wzmocniona przez AxonFlow oraz HackerNoonowy Governance Layer for Claude Code, to jeden folder na hook, jeden plik metadanych na folder, język-niezależnie. Hook zwraca allow/deny/modify przez exit code lub stdout JSON.

acme/agent-hooks/
├── bootstrap.sh
├── version.json
├── .github/CODEOWNERS # security team jest ownerem /pre-tool-use/**
└── hooks/
├── deny-dangerous-commands/{hook.json, run.sh, policy.yaml, tests/}
├── auto-format/{hook.json, run.sh}
├── cost-cap/{hook.json, run.py}
└── tag-ai-pr/{hook.json, run.sh}

Minimalny hook.json:

{
"name": "deny-dangerous-commands",
"event": "PreToolUse",
"matcher": "Bash",
"command": "./run.sh",
"timeout_ms": 1500,
"owner": "security@acme.dev"
}

Dla hooków PreToolUse security team jest w CODEOWNERS, bo to są hooki z mocą po cichu osłabiać każdą inną kontrolę.

Wzorzec, który się przyjął — w referencji z dotfiles.github.io i nowoczesnych dotfiles repo organizacji:

#!/usr/bin/env bash
# ~/.acme-dotfiles/bootstrap.sh — day-1 i logowanie do shella
set -euo pipefail
HOOKS_DIR="${HOME}/.acme-agent-hooks"
SETTINGS="${HOME}/.claude/settings.json"
if [ ! -d "${HOOKS_DIR}" ]; then
git clone --depth 50 https://github.com/acme/agent-hooks "${HOOKS_DIR}"
else
git -C "${HOOKS_DIR}" fetch --quiet origin main
git -C "${HOOKS_DIR}" merge --ff-only origin/main
fi
# Zweryfikuj podpisane commity (defense in depth)
if ! git -C "${HOOKS_DIR}" verify-commit HEAD >/dev/null 2>&1; then
echo "FATAL: ${HOOKS_DIR} HEAD nie jest podpisany. Odmawiam instalacji."
exit 1
fi
# Wyrenderuj settings.json z metadanych (idempotentnie)
"${HOOKS_DIR}/bin/render-settings.py" "${HOOKS_DIR}/hooks/" > "${SETTINGS}.tmp"
mv "${SETTINGS}.tmp" "${SETTINGS}"
mkdir -p "${HOME}/.acme-hooks" && touch "${HOME}/.acme-hooks/audit.log"

Trzy właściwości mają znaczenie: idempotentność (drift niemożliwy — ręczne edycje są nadpisywane przy następnym logowaniu), weryfikacja-najpierw (fail-closed default; zmodyfikowane repo odmawia instalacji), i read-only po stronie developera (edycje idą przez PR, bootstrap jest jedynym źródłem prawdy).

Trzy podejścia działają w 2026:

(a) Podpisane commity git. Najlżejszy setup. Branch-protect main, wymagaj podpisanych commitów, CODEOWNERS routuje PR PreToolUse do security. Bootstrap uruchamia git verify-commit HEAD i odmawia instalacji przy failu. Plusy: zero nowej infrastruktury. Minusy: weryfikuje commit, nie executable — skradziony klucz signing omija kontrolę do rotacji.

(b) Sigstore / cosign attached signatures. Cięższe, silniejsze. CI podpisuje każdy hook (lub tarball hooks/) przez cosign używając keyless lub OIDC tożsamości organizacji. Bootstrap wywołuje cosign verify-blob --certificate-identity=ci@acme.dev. Wiąże sygnaturę z tożsamością pipeline’u CI, więc skradziony klucz developera sam nie wystarczy. Integruje się z SLSA provenance.

(c) Releasy podpisane PGP. Tradycyjne podejście dystrybucji linuxowych. CI buduje i podpisuje PGP hooks-2026.05.21.tar.gz. Bootstrap pobiera i gpg --verify. Dobrze zrozumiałe, offline-verifiable; PGP key management jest słynnie bolesny i większość zespołów po kilku miesiącach przechodzi na (b).

Większość zespołów zaczyna od (a) w week-1 i dodaje (b) w ciągu kwartału.

Audyt (logowanie każdego wykonania hooka, okresowy review)

Dział zatytułowany „Audyt (logowanie każdego wykonania hooka, okresowy review)”

Każdy skrypt hooka opakowuje swój body w logging shim piszący linię JSON do ~/.acme-hooks/audit.log:

hooks/deny-dangerous-commands/run.sh
#!/usr/bin/env bash
input="$(cat)"
cmd="$(echo "$input" | jq -r '.tool_input.command // empty')"
decision="allow"; reason=""
if [[ "$cmd" =~ rm[[:space:]]+-rf[[:space:]]+/ ]]; then
decision="deny"; reason="destrukcyjne rekurencyjne usunięcie na root"
fi
jq -n --arg ts "$(date -u +%FT%TZ)" --arg hook "deny-dangerous-commands" \
--arg cmd "$cmd" --arg decision "$decision" --arg reason "$reason" \
'{ts:$ts, hook:$hook, cmd:$cmd, decision:$decision, reason:$reason}' \
>> "${HOME}/.acme-hooks/audit.log"
[ "$decision" = "deny" ] && { echo "{\"continue\": false, \"reason\": \"$reason\"}"; exit 0; }
echo '{"continue": true}'

Audit log pozwala odpowiedzieć na pytania, które mają znaczenie, gdy coś się psuje: czy jakiś hook zablokował wywołanie narzędzia w ostatniej godzinie? Czy nowy cost-cap zatriggerował się na maszynie Mary przed otwarciem buga? Czy jakikolwiek hook zakończył non-zero od ostatniego release? Bez logu wszystko staje się zgadywaniem.

Okresowy review to druga połowa. Przynajmniej kwartalnie security lub platform engineer skanuje zagregowane dane (denials na hook, error rate, hooki które nigdy nie odpaliły). Hooki z zerem hitów są kandydatami do usunięcia. Wysokie error rate wymaga buga przeciwko ownerowi. Zaskakujące wzorce denial to sygnał incydentu.

Przydatne współdzielone hooki (auto-format, AI-PR label, dangerous-command deny, cost cap)

Dział zatytułowany „Przydatne współdzielone hooki (auto-format, AI-PR label, dangerous-command deny, cost cap)”

Starter set spłacający koszt governance w jednym sprincie:

  • PreToolUse · deny-dangerous-commands. Regex-blokuj rm -rf /, dd of=/dev/, fork bomby, curl ... | bash przeciwko nieznanym hostom. Deterministyczne, jailbreak-proof, ratuje przynajmniej jeden produkcyjny incydent na zespół na kwartał.
  • PreToolUse · redact-secrets. Skanuj wejścia narzędzi pod kątem kluczy AWS/GitHub/OpenAI/Anthropic/Stripe. Blokuj z wiadomością “to wygląda na sekret, wklej z password managera”. Łapie agenta wklejającego prawdziwy klucz przez przypadek.
  • PreToolUse · cost-cap. Czytaj plik daily-rolling spend. Powyżej per-dev capa blokuj kosztowne wywołania. Najsilniejsza kontrola kosztów, jaka istnieje — provider-agnostic.
  • PostToolUse · auto-format. Po każdym Edit/Write uruchom prettier --write / gofmt / ruff format. Usuwa hałas review “agent wprowadził niespójne formatowanie” całkowicie.
  • PostToolUse · run-typecheck. Przy każdym zapisie pliku TS, tsc --noEmit --incremental i podaj błędy z powrotem do następnego turnu agenta. Agent sam fiksuje swoje błędy typów przed review.
  • Stop · tag-ai-pr. Oznacz PR labelem ai-authored (Q11). Driver pipeline’u dodatkowych gatów.
  • Stop · open-pr. Push i gh pr create, gdy agent mówi, że skończył. Zamyka lukę “agent napisał kod, którego nikt nie zreview’ował”.
  • SessionStart · skills-sync / hooks-sync. Auto-update repo skills i hooków, by developerzy nigdy nie byli na nieaktualnej konfiguracji.

Każdy hook robi jedną rzecz, ide przez repo, ma ownera, działa pod audytem. Dziesięcioliniowy bash auto-formatter jest ok. Dziesięcioliniowy auto-formatter, który po cichu eksfiltruje env vars, to problem, który governance zatrzymuje.

  1. Zasiej repo dwoma bezpiecznymi hookami. Wybierz dwa z oczywistym upside i małym promieniem rażenia: PostToolUse · auto-format i Stop · tag-ai-pr. Żaden nie psuje pracy devów przy misfire. PR, merge.

  2. Podłącz dotfiles bootstrap na własnej maszynie. Dodaj skrypt powyżej do repo dotfiles. Uruchom. Potwierdź, że ~/.claude/settings.json zostało przepisane i oba hooki są listowane w /hooks. Zepsuj (ręcznie edytuj settings.json), re-uruchom, potwierdź samonaprawę.

  3. Włącz podpisane commity, branch protection, CODEOWNERS. Branch-protect main, wymagaj podpisanych commitów i review PR. Routuj hooks/pre-tool-use/** do security, post-tool-use/** i stop/** do platform. Dodaj git verify-commit HEAD do bootstrap. Niepodpisany commit na main to teraz deploy-blocking incident.

  4. Dodaj audit log i mały dashboard. Opakuj każdy hook loggingiem shimem JSON-line. Pipuj audit.log do centralnego loggingu. Zbuduj jednostronicowy dashboard: wykonania na dzień, denials na dzień na hook, error rate, hooki z zerem hitów w 30 dniach. Bez niego nie spojrzysz; z nim dane napędzają decyzje.

  5. Rolluj najpierw do jednego zespołu, nie organizacji. Pięć do dziesięciu inżynierów. Usiądź z nimi przez bootstrap. Patrz, co się psuje (proxy VPN firmowe, niestandardowy $HOME, Windows + WSL). Patrz na audit log pod kątem false-positive denials — każdy to bug hooka, nie problem developera. Potem rozszerzaj.

  6. Dodaj high-value hooki PreToolUse, gdy zaufanie zbudowane. Po dwóch czystych tygodniach auto-format dodaj deny-dangerous-commands i redact-secrets. Wypchnij za feature flagiem (pole disabled: true, które developer może przełączyć na godzinę). Patrz na log dwa tygodnie. Gdy blokują prawdziwe złe inputy i nigdy nie legitnej roboty, usuń disable hatch.

  7. Dodaj cost capy i SessionStart sync. Gdy podstawy stabilne, dodaj cost-cap (Q4) i SessionStart · hooks-sync / skills-sync (Q6). Pierwszy daje finansom off-switch; drugi sprawia, że następny update dociera do każdej maszyny bez wiadomości na Slacku.

  8. Rotuj klucze, review’uj kwartalnie. Calendar event: co kwartał platform rotuje klucze signing, audytuje CODEOWNERS pod kątem nieaktualnych członków, uruchamia git log --since=... na repo hooków, by potwierdzić, że każda zmiana przeszła przez review PR. Niepodpisany merge lub usunięta linia audytu to incident, nie finding.

Niepodpisane hooki. Zespół kopiuje hooka z blogposta bezpośrednio do ~/.claude/hooks/. Sześć miesięcy później ktoś forkuje dotfiles, podmienia złośliwą wersję, i następny bootstrap.sh instaluje ją na każdej maszynie. Fix: uczyń niepodpisane hooki nieuruchamialnymi. Jeśli Twój bootstrap zainstaluje cokolwiek znajdzie, nie masz governance hooków.

Brak audytu. “Mamy hooki, ale nic nie logujemy.” Hooki działają większość czasu, ale raz, gdy hook zaczyna blokować legitną robotę, dowiadujesz się przez DM na Slacku, nie przez dashboard, którego nie zbudowałeś. Audit log to jedna linia jq na hook.

Drift hooków. Każdy developer ręcznie edytujący settings.json kończy z dwiema maszynami uruchamiającymi tę samą konfigurację. Wzorzec bootstrap-on-every-login to fixuje — ale tylko jeśli developerzy nie mogą sprawić, by lokalne edycje przetrwały. Wykarbuj ~/.claude/hooks.local/ dla personalnych hooków i zostaw to w spokoju.

Blokowanie devów. Buggy PreToolUse blokujący legitne wywołania jest gorszy niż brak hooka. Devowie obejdą go — odinstalując, wyłączając lub przerzucając się na narzędzie. Zawsze pchnij PreToolUse za feature flagiem przez dwa tygodnie. Zawsze udokumentowany escape hatch. Każdy false denial to bug P1 przeciwko ownerowi.

Hooki potrzebujące sieci. Hook wywołujący wolne API przy każdym wywołaniu czyni agenta nieużywalnym. Hooki są sub-100ms lub fire-and-forget. timeout_ms 1.5s w hook.json istnieje z tego powodu; audit log powinien trackować p95 duration.

Zapominanie o Windows / WSL. Skrypty bootstrap na macOS często zakładają bash i /usr/local. Testuj na każdej wspieranej platformie przed deklaracją zakończenia rolloutu.

Hooki bez ownera. Hook bez ownera decay’uje w momencie odejścia autora. Hooki bez ownerów są archiwowane przy następnym kwartalnym review — bez wyjątków.

Mieszanie personalnych i firmowych hooków. Zawsze namespace’uj firmowe pod ~/.claude/hooks/.acme/ i zostaw resztę w spokoju. git pull nigdy nie powinien clobberować personalnych eksperymentów.

  • Firmowe repo agent-hooks istnieje, z przynajmniej 5 produkcyjnymi hookami.
  • Każdy folder hooka ma hook.json deklarujące event, matcher, ownera, timeout.
  • Repo ma branch protection na main, wymaga podpisanych commitów, ma CODEOWNERS routujące PreToolUse do security.
  • bootstrap.sh żyje we współdzielonym dotfiles. Klonuje, weryfikuje podpisane commity, deterministycznie przepisuje ~/.claude/settings.json.
  • Bootstrap jest idempotentny — re-uruchomienie na maszynie z ręcznymi edycjami settings.json przywraca stan kanoniczny.
  • Bootstrap jest częścią day-1 setup nowego hire. Nowi inżynierowie wchodzą z wszystkimi hookami zainstalowanymi i zweryfikowanymi.
  • Każde wykonanie hooka pisze linię JSON do ~/.acme-hooks/audit.log, streamowaną do centralnego loggingu.
  • Wewnętrzny dashboard pokazuje wykonania na dzień, denials na dzień, p95 duration, hooki z zerem hitów.
  • Udokumentowany kwartalny review rotuje klucze signing, audytuje CODEOWNERS, archiwizuje nieaktualne hooki, review’uje dashboard.
  • Przynajmniej jeden PreToolUse · deny-dangerous-commands jest w produkcji z przynajmniej jednym zalogowanym prawdziwym denial w ostatnim kwartale.
  • Otwarcie świeżej sesji Claude Code na nowej maszynie i uruchomienie /hooks listuje te same hooki, co wszędzie indziej w organizacji.