Ana içeriğe atla

title: Mimari description: “Hook handler, config yükleme ve policy değerlendirmesinin dahili olarak nasıl çalıştığı” icon: sitemap

Bu belge failproofai’nin dahili işleyişini açıklar: hook sistemi agent tool çağrılarını nasıl keser, yapılandırma nasıl yüklenir ve birleştirilir, policiler nasıl değerlendirilir ve dashboard agent aktivitesini nasıl izler.

Genel Bakış

failproofai iki bağımsız alt sistemden oluşur:
  1. Hook handler - Claude Code’un her agent tool çağrısında invoke ettiği hızlı bir CLI alt işlemi. Policileri değerlendirir ve bir karar döner.
  2. Agent Monitor (Dashboard) - Agent oturumlarını izlemek ve policileri yönetmek için bir Next.js web uygulaması.
Her iki alt sistem ~/.failproofai/ ve projenin .failproofai/ dizininde yapılandırma dosyalarını paylaşır, ancak ayrı işlemler olarak çalışırlar ve yalnızca dosya sistemi üzerinden iletişim kurarlar.

Hook handler

Claude Code ile Entegrasyon

failproofai policies --install çalıştırdığınızda, ~/.claude/settings.json dosyasına şu gibi girişler yazar:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "failproofai --hook PreToolUse"
          }
        ]
      }
    ],
    "PostToolUse": [ ... ]
  }
}
Claude Code daha sonra her tool çağrısından önce failproofai --hook PreToolUse komutunu bir alt işlem olarak invoke eder, stdin üzerinde bir JSON payload iletir.

Payload formatı

{
  "session_id": "abc123",
  "transcript_path": "/home/user/.claude/projects/myproject/sessions/abc123.jsonl",
  "cwd": "/home/user/myproject",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": { "command": "sudo apt install nodejs" }
}
PostToolUse olayları için, payload ayrıca tool’un çıktısını içeren tool_result öğesini içerir. Handler 1 MB’lık stdin sınırını zorunlu kılar. Bu limiti aşan payload’lar atılır ve tüm policiler zımnen izin verir.

Yanıt formatı

Reddet (PreToolUse):
{
  "hookSpecificOutput": {
    "permissionDecision": "deny",
    "permissionDecisionReason": "Blocked by failproofai: sudo command blocked"
  }
}
Reddet (PostToolUse):
{
  "hookSpecificOutput": {
    "additionalContext": "Blocked by failproofai because: API key detected in output"
  }
}
Talimat (Stop dışındaki herhangi bir olay):
{
  "hookSpecificOutput": {
    "additionalContext": "Instruction from failproofai: Verify tests pass before committing."
  }
}
Stop olayı talimatı:
  • Çıkış kodu: 2
  • Neden stderr’e yazılır (stdout’a değil)
İzin Ver:
  • Çıkış kodu: 0
  • Boş stdout
İzinle mesaj: allow(message), bir policy işleme izin verildiğinde bile Claude’a bilgilendirici bağlam gönderebilir. Hook handler aşağıdaki JSON’u stdout’a yazar (bir config dosyasına değil — bu handler’ın Claude Code’a yanıtıdır, reddet ve talimat yanıtları gibi):
// Hook handler işlemi tarafından stdout'a yazılır
{
  "hookSpecificOutput": {
    "additionalContext": "All CI checks passed on branch 'feat/my-feature'."
  }
}
  • Çıkış kodu: 0 (işleme izin verilir)
  • Birden çok policy allow ile mesaj döndüğünde, mesajları satır sonlarıyla birleştirilmiş bir additionalContext dizesine katılır
  • Hiçbir policy mesaj sağlamıyorsa, stdout boştur (öncekiyle aynı)

İşleme hattı

src/hooks/handler.ts tam hattı uygular:
stdin JSON
  → parse payload (max 1 MB)
  → extract session metadata (session_id, cwd, tool_name, tool_input, etc.)
  → readMergedHooksConfig(cwd)    ← merges project + local + global config
  → register enabled builtin policies with resolved params
  → load custom policies from customPoliciesPath (if set)
  → register custom policies into policy registry
  → evaluate all policies (builtins first, then custom)
      → first deny short-circuits
      → instruct decisions accumulate
      → allow messages accumulate
  → write JSON decision to stdout
  → persist event to ~/.failproofai/hook-activity.jsonl
  → exit
Tipik payload’lar için tüm işlem LLM çağrıları olmadan 100ms altında çalışır.

Yapılandırma yükleme

src/hooks/hooks-config.ts üç kapsamlı config yüklemeyi uygular.
[1] {cwd}/.failproofai/policies-config.json        ← project  (highest priority)
[2] {cwd}/.failproofai/policies-config.local.json  ← local
[3] ~/.failproofai/policies-config.json             ← global   (lowest priority)
Birleştirme mantığı:
  • enabledPolicies - üç dosya arasında birleştirilmiş tekil union
  • policyParams - policy başına anahtar, bunu tanımlayan ilk dosya tamamen kazanır
  • customPoliciesPath - bunu tanımlayan ilk dosya kazanır
  • llm - bunu tanımlayan ilk dosya kazanır
Web dashboard, project cwd ile invoke edilmediği için okuma ve yazma için readHooksConfig() (yalnızca global) kullanır.

Policy değerlendirmesi

src/hooks/policy-evaluator.ts policileri sırayla çalıştırır. Her policy için:
  1. Policy’nin params şemasını ara (varsa).
  2. Birleştirilmiş config’ten policyParams[policy.name] oku.
  3. Kullanıcı tarafından sağlanan değerleri schema varsayılanları üzerine birleştirerek ctx.params oluştur.
  4. Çözümlenen context ile policy.fn(ctx) çağır.
  5. Sonuç deny ise, hemen dur ve bu kararı döndür.
  6. Sonuç instruct ise, mesajı birikdir ve devam et.
  7. Sonuç allow ise, sonraki policiye geç.
Tüm policiler çalıştıktan sonra:
  • Herhangi bir deny döndürülüyse, deny yanıtını yayınla.
  • Herhangi bir instruct dönüşü toplanmışsa, tüm mesajlar birleştirilmiş tek bir instruct yanıtı yayınla.
  • Aksi halde, allow yanıtını yayınla (boş stdout, exit 0).

Yerleşik policiler

src/hooks/builtin-policies.ts 26 yerleşik policy’yi BuiltinPolicyDefinition nesneleri olarak tanımlar:
interface BuiltinPolicyDefinition {
  name: string;
  description: string;
  fn: (ctx: PolicyContext) => PolicyResult;
  match: {
    events: HookEventType[];
    tools?: string[];
  };
  defaultEnabled: boolean;
  category: string;
  beta?: boolean;
  params?: PolicyParamsSchema;
}
params kabul eden policiler, her parametre için türler ve varsayılanlar ile bir PolicyParamsSchema bildirirler. Policy değerlendiricisi çözümlenen değerleri fn çağrılmadan önce ctx.params içerisine enjekte eder. Policy fonksiyonları ctx.params okur, null kontrol etmez çünkü varsayılanlar her zaman önce uygulanır. Policilerin içindeki desen eşleştirmesi, ham string eşleştirmesinde değil, ayrıştırılmış command token’larını (argv) kullanır. Bu, shell operatörü enjeksiyonu yoluyla bypass’ı engeller (örn. sudo systemctl status * için bir desen ; ekleyerek bypass edilemez rm -rf /`).

Özel policiler

src/hooks/custom-hooks-registry.ts globalThis destekli bir registry uygular:
const REGISTRY_KEY = "__failproofai_custom_hooks__";

export const customPolicies = {
  add(hook: CustomHook): void { ... }
};

export function getCustomHooks(): CustomHook[] { ... }
export function clearCustomHooks(): void { ... }  // used in tests
src/hooks/custom-hooks-loader.ts kullanıcı policy dosyasını yükler:
  1. Config’ten customPoliciesPath oku; yoksa atla.
  2. Mutlak yola çöz; dosyayı kontrol et var mı.
  3. Tüm from "failproofai" importlarını gerçek dist path’e yeniden yaz böylece customPolicies aynı globalThis registry’ye çözülür.
  4. ESM uyumluluğunu sağlamak için geçişli yerel importları yinelemeli olarak yeniden yaz.
  5. Geçici .mjs dosyaları yaz ve giriş dosyasını import() et.
  6. Kayıtlı hook’ları almak için getCustomHooks() çağır.
  7. Tüm temp dosyaları bir finally bloğunda temizle.
Herhangi bir hata (dosya bulunamadı, söz dizimi hatası, import başarısızlığı) durumunda, hata ~/.failproofai/hook.log dosyasına kaydedilir ve loader boş bir dizi döner. Yerleşik policiler etkilenmez. Özel policiler tüm yerleşik policilerden sonra değerlendirilir. Bir özel policy deny yine de diğer özel policileri kısa devreye soksa da (yerleşik policilerin tümü o noktada zaten çalıştırılmıştır).

Aktivite Logging

Her hook olayından sonra, handler ~/.failproofai/hook-activity.jsonl dosyasına bir JSONL satırı ekler:
{
  "timestamp": "2026-04-06T12:34:56.789Z",
  "sessionId": "abc123",
  "eventType": "PreToolUse",
  "toolName": "Bash",
  "policyName": "block-sudo",
  "decision": "deny",
  "reason": "sudo command blocked by failproofai",
  "durationMs": 12
}
Non-allow kararı alan her policy için bir satır. Allow kararları kaydedilmez (dosya küçük tutmak için).

Dashboard mimarisi

Dashboard, App Router ile React Server Components ve Server Actions kullanan bir Next.js 16 uygulamasıdır.
app/
  layout.tsx                  ← Root layout (theme, telemetry, nav)
  projects/page.tsx           ← Server component: list all Claude projects
  project/[name]/page.tsx     ← Server component: list sessions in a project
  project/[name]/session/
    [sessionId]/page.tsx      ← Server component: render session viewer
  policies/page.tsx           ← Client component: policy management + activity log
  actions/
    get-hooks-config.ts       ← Read config + policy list
    update-hooks-config.ts    ← Toggle policy on/off
    update-policy-params.ts   ← Update policy parameters
    get-hook-activity.ts      ← Paginate/search activity log
    install-hooks-web.ts      ← Install/remove hooks from the browser
  api/
    download/[project]/[session]/route.ts   ← Export session as ZIP/JSONL
Veri akışı:
  • Sayfa bileşenleri proje/oturum verilerini doğrudan dosya sisteminden okumak için lib/projects.ts ve lib/log-entries.ts çağırır (okumalar için API katmanı yok).
  • Policies sayfası tüm mutasyonlar (toggle, params güncelleme, install/remove) için Server Actions kullanır.
  • Oturum viewer’ı Claude’un JSONL transkript formatını ayrıştırır ve mesajlar ile tool çağrılarının bir zaman çizelgesini renderler.
Temel tasarım kararları:
  • Veritabanı yok - tüm kalıcı state düz dosyalardadır (~/.failproofai/, ~/.claude/projects/).
  • Mutasyonlar için Server Actions - CRUD işlemleri için REST API gerekli değil.
  • Okuma sayfaları için React Server Components - daha hızlı ilk yükleme, veri getirme için client bundle yok.
  • Client bileşenleri sadece etkileşimlilik gerekli yerlerde (policy toggle’ları, aktivite arama, log viewer).

Dosya düzeni

failproofai/
├── bin/
│   └── failproofai.mjs           # CLI router (hook / dashboard / install / etc.)
├── src/hooks/
│   ├── handler.ts                # Hook event pipeline
│   ├── builtin-policies.ts       # 26 policy definitions
│   ├── policy-evaluator.ts       # Policy execution engine
│   ├── policy-registry.ts        # Policy registration and lookup
│   ├── policy-types.ts           # TypeScript interfaces
│   ├── hooks-config.ts           # Multi-scope config loading
│   ├── custom-hooks-registry.ts  # globalThis-backed hook registry
│   ├── custom-hooks-loader.ts    # ESM loader for user JS hooks
│   ├── manager.ts                # install / remove / list operations
│   ├── install-prompt.ts         # Interactive policy selection prompt
│   ├── hook-logger.ts            # Logging to hook.log
│   ├── hook-activity-store.ts    # Persist activity to hook-activity.jsonl
│   └── llm-client.ts             # LLM API client (for AI-powered policies)
├── app/                          # Next.js dashboard (pages + server actions)
├── lib/                          # Shared utilities
│   ├── projects.ts               # Enumerate Claude projects from filesystem
│   ├── log-entries.ts            # Parse Claude transcript JSONL format
│   ├── paths.ts                  # Resolve system paths
│   └── ...
├── components/                   # Shared React UI components
├── contexts/                     # React context providers (theme, auto-refresh, telemetry)
├── examples/                     # Example custom hook files
└── __tests__/                    # Unit and E2E tests