Runtime governance engine for AI coding agents. Chitin intercepts tool calls from agent drivers (Claude Code, GitHub Copilot, Codex, Gemini), normalizes them into six canonical action types, evaluates them against deny-first policy rules and safety invariants, and emits allow/deny decisions back to the driver.
flowchart LR
A[Agent Driver] -->|tool call| B[Hook]
B -->|parse env vars| C[Normalize]
C -->|Action| D[Invariant Check]
D -->|violations?| E[Policy Evaluate]
E -->|Decision| F{Allow / Deny}
F -->|allow| G[exit 0]
F -->|deny| H[exit 2 + JSON]
Hook (internal/hook/) -- Reads tool call data from driver-specific environment variables (CLAUDE_HOOK_EVENT_NAME, COPILOT_HOOK_EVENT, etc.) and dispatches to the evaluation pipeline. Read-only actions are fail-open; missing policy files are fail-closed.
Normalize (internal/normalize/) -- Maps each tool call to one of six canonical ActionType values: read, write, exec, git, net, dangerous. Uses the canon package to parse shell commands and classify by tool, flags, and patterns.
Invariant Check (internal/invariant/) -- Runs registered safety invariants before policy evaluation. Invariants cannot be overridden by policy rules; they can only be set to monitor (warn) or off per invariant in chitin.yaml. Built-in invariants include recursive-delete guard, permission escalation detection, network egress restriction, IDE socket access prevention, destructive SQL detection, and dynamic script execution tracking.
Policy Evaluate (internal/policy/) -- Two-phase evaluation against chitin.yaml rules. Phase 1: any matching deny rule blocks (deny always wins). Phase 2: any matching allow rule permits. Phase 3: no match means default deny (fail-closed). Policy mode can be enforce (block) or monitor (warn only).
Event Emit (internal/hook/emit.go) -- Appends every governance decision as a JSON line to .chitin/events.jsonl for telemetry ingestion.
- Go 1.18+
- An AI coding agent: Claude Code, GitHub Copilot, OpenAI Codex, or Google Gemini
From source:
go install github.com/chitinhq/chitin/cmd/chitin@latestOr via the install script:
curl -fsSL https://raw.githubusercontent.com/chitinhq/chitin/main/install.sh | bashInitialize hooks and a starter policy for your agent driver:
chitin init claude # or: copilot, codex, geminiThis writes three files:
.claude/settings.json(or equivalent for your driver) -- hooks configchitin.yaml-- starter policy with deny rules for dangerous ops, protected branches, and secrets.chitin-identity-- agent identity file
Check that everything is wired up:
chitin statusTest how a specific action would be evaluated without executing it:
chitin evaluate -t Bash -c "rm -rf /tmp/data"
# Tool: Bash
# Action: dangerous
# Decision: DENY
# Reason: Dangerous operations require human approvalchitin evaluate -t Bash -c "git status"
# Tool: Bash
# Action: git
# Decision: ALLOWValidate a policy file:
chitin validate chitin.yamlPolicies are defined in chitin.yaml at the project root:
mode: enforce # or "monitor" (warn without blocking)
rules:
- action: dangerous
effect: deny
reason: Dangerous operations require human approval
- action: git
branches: [main, master]
effect: deny
reason: Direct push to protected branch
- action: write
target: ".env"
effect: deny
reason: Secrets files must not be modified by agents
- action: "*"
effect: allow
reason: Default allow for non-restricted actions
invariantModes:
no-network-egress: monitor # warn but don't block
script-execution-tracking: off # disable this checkAction types for rules: read, write, exec, git, net, dangerous, *.
github.com/chitinhq/chitin/canon -- Shell command canonicalization with equivalence digests. Parses raw shell strings into structured, normalized forms. Semantically equivalent commands produce identical digests.
package main
import (
"fmt"
"github.com/chitinhq/chitin/canon"
)
func main() {
// Equivalent commands produce the same canonical form and digest.
cat := canon.ParseOne("cat foo.txt")
head := canon.ParseOne("head foo.txt")
fmt.Println(cat.Tool) // "read"
fmt.Println(head.Tool) // "read"
fmt.Println(cat.Digest == head.Digest) // true
// Tool aliases, flag normalization, and subcommand extraction.
cmd := canon.ParseOne("rg -n pattern src/")
fmt.Println(cmd.Tool) // "grep"
fmt.Println(cmd.Args) // [pattern src/]
// Pipeline parsing with chain operators.
pipeline := canon.Parse("git add . && git commit -m 'fix'")
fmt.Println(len(pipeline.Segments)) // 2
fmt.Println(pipeline.Segments[1].Op) // "&&"
// Sensitive values are automatically masked.
secret := canon.ParseOne("curl -H 'Authorization: Bearer sk-longtoken1234567890abcdefghij' http://api.example.com")
// Long token-like args replaced with [MASKED]
}Features:
- 60+ tool aliases (e.g.,
cat/head/tail->read,rg/ag/ack->grep) - Short-to-long flag normalization (
-r->recursive,-i->ignore-case) - Deterministic SHA256 digests (first 16 hex chars) for deduplication
- Chain and pipe parsing (
&&,||,;,|) - Automatic masking of secrets and API keys in arguments
# Build
go build ./cmd/chitin
# Run tests
go test ./...
# Run tests for a specific package
go test ./canon/
# Vet
go vet ./...See LICENSE.