- Go 99.3%
- Makefile 0.7%
| checks | ||
| cmd/openearth-gryph | ||
| .gitignore | ||
| go.mod | ||
| go.sum | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
openearth-gryph
Apache-2.0 Go binary that composes gryph with additional security checks that enforce the IAS gates protocol at Claude Code (and other supported agent) hook time.
What it does?
gryph is a hook-based interceptor for AI coding agents. Its Check interface lets implementers enforce organisation-specific rules at each tool call. openearth-gryph ships four such Checks that machine-enforce the IAS gates:
| Check | Enforces | Decision |
|---|---|---|
gate1_supplychain |
Gate 1 — "Before touching any external component, ensure it is assimilated (read + understood)" | Guidance (advisory) if a referenced component has assimilation.level < 2 in supply-chain.json |
gate3_dockerbuild |
Gate 3 — "Use docker compose build, never standalone docker build" |
Block on ^docker\s+build commands |
gate4_curl |
Gate 4 — "Never curl -sf; capture HTTP status separately" |
Guidance on curl -sf / curl -fs / curl --silent --fail |
condoc_pair |
CONDOC — "Code and docs update together" | Guidance when .py/.go/.rs/.kt/.ts is written without any .md touched this session |
The binary is a drop-in replacement for gryph in agent hook configs: openearth-gryph _hook claude-code PreToolUse. All gryph subcommands (logs, status, export, stats, cost, …) continue to work because openearth-gryph calls gryph's cli.Execute().
Cross-repo / universal binary
openearth-gryph is installed once per user (~/.local/bin/ + ~/.claude/settings.json hooks) and observes every Claude Code session across every repo. Each Check walks up from event.WorkingDirectory to find an IAS-root marker (config/supply-chain.json + STATEOFCLAUDE.md). In repos that have opted into IAS governance, Checks enforce; in repos that haven't, Checks silently pass through. Result: one install, opt-in enforcement per repo, per-repo config.
Two-level compliance architecture
openearth-gryph is one half of a pair:
- API proxy (separately maintained) observes Claude's reasoning at the Anthropic API boundary
- openearth-gryph observes Claude's actions at the Claude Code runtime boundary
Reconciling the two streams catches drift — an agent that plans one thing but does another. Designed for compliance-bound deployments where the full evidence chain (reasoning → action → audit) must be reproducible.
Architecture
Claude Code hook
↓ stdin JSON
openearth-gryph _hook claude-code <type>
↓
gryph's cli.Execute() ──→ gryph's NewApp() ──→ init() hooks:
↓
our Checks registered
↓
gryph's evaluator loop
↓
SQLite audit + hook response
No fork, no patches to gryph's core. openearth-gryph imports github.com/safedep/gryph/cli and uses its extension registry (see upstream PR #TBD) to add our Checks at init() time.
Building
make build # current OS → bin/openearth-gryph (34-35 MB)
make build-linux # linux/amd64 → bin/openearth-gryph-linux-amd64 (for Docker)
make test # Run Check unit tests
Installing — host
make install # Copies bin/openearth-gryph to ~/.local/bin
Then update ~/.claude/settings.json to replace every "gryph " in hook command fields with "openearth-gryph ". Back up the settings file first:
cp ~/.claude/settings.json ~/.claude/settings.json.pre-openearth-gryph
# Preview the change:
grep -n '"gryph' ~/.claude/settings.json
# Apply (macOS/BSD sed):
sed -i.bak 's|"gryph |"openearth-gryph |g' ~/.claude/settings.json
# Verify:
grep -n '"openearth-gryph' ~/.claude/settings.json
Start a new Claude Code session after editing; openearth-gryph will now receive every hook event and run our Checks on top of gryph's own evaluator. Rollback is cp ~/.claude/settings.json.pre-openearth-gryph ~/.claude/settings.json.
Installing — Docker dev container
Cross-compile the linux binary on the host, then copy it into a running container. Example with a docker-compose stack exposing a dev service named <devservice>:
# 1. Produce the linux/amd64 binary on the host
make build-linux
# 2. Copy into the running container
docker compose cp bin/openearth-gryph-linux-amd64 <devservice>:/usr/local/bin/openearth-gryph
# 3. Permissions: docker cp lands as root; the binary needs +x and,
# if gryph was previously installed as root into the image, the
# user's .local tree needs to be chown'd so SQLite can create the
# audit DB under $HOME/.local/share/gryph/.
docker compose exec -T -u root <devservice> chmod +x /usr/local/bin/openearth-gryph
docker compose exec -T -u root <devservice> chown -R <dev-user>:<dev-user> /home/<dev-user>/.local
# 4. Smoke test
docker compose exec -T <devservice> openearth-gryph version
The SQLite ownership step is the common gotcha: without it, the first _hook invocation inside the container fails with unable to open database file (14) because $HOME/.local/share/gryph/ was created as root during image build.
For a permanent fix, the Dockerfile used to build <devservice> should pre-create the directory with the correct owner:
RUN install -d -o <dev-user> -g <dev-user> /home/<dev-user>/.local/share/gryph
Testing the hook contract without Claude Code
Any gryph-compatible agent hook is a stdin-JSON / stdout + stderr / exit-code protocol. You can exercise openearth-gryph end-to-end — including our custom Checks and gryph's own event pipeline — by piping a synthetic PreToolUse event directly into the binary:
printf '%s' '{
"session_id":"smoke-1",
"transcript_path":"/tmp/smoke.jsonl",
"cwd":"/path/to/your/ias-governed/repo",
"hook_event_name":"PreToolUse",
"tool_name":"Bash",
"tool_use_id":"t1",
"tool_input":{"command":"docker build -t foo ."}
}' | openearth-gryph _hook claude-code PreToolUse
echo "exit=$?"
Expected responses:
Command in tool_input.command |
In IAS-root cwd |
Result |
|---|---|---|
docker build -t foo . |
yes | exit 2, Gate 3 block text on stderr |
curl -sf https://example.com/ |
yes | exit 0, Gate 4 guidance text on stderr |
curl https://example.com/ |
yes | exit 0, silent |
docker build -t foo . |
no (non-IAS cwd) |
exit 0, silent — pass-through |
This works identically on the host binary and the Linux binary inside Docker, and is the fastest way to verify a fresh install before pointing Claude Code at it.
Upstream dependencies — PRs in flight
This module depends on two upstream PRs to github.com/safedep/gryph:
-
feat/claude-guidance-output— completes gryph's three-valued security decision model (Allow/Block/Guidance) by wiring the existing but currently-unusedDecisionGuidancethrough to Claude Code's hook output protocol (exit 0 + stderr). Without this PR,gate1_supplychain,gate4_curl, andcondoc_pairwould have to useBlockinstead of the semantically correctGuidance. -
feat/extension-point-check-factory— adds a package-levelcli.RegisterCheckFactory(f func(*config.Config) security.Check)so external binaries composing gryph as a library can register additional Checks without modifyingcli/root.go. Idiomatic Go (same pattern asdatabase/sql.Register,image/pngdecoder registration).
While those PRs are in review, go.mod uses a replace directive pointing at a local gryph fork carrying both PR branches. Once upstream merges, the replace is removed and we pin a release tag.
License
Apache-2.0. See LICENSE.
Layout
openearth-gryph/
├── checks/ # Our Check implementations + unit tests
├── cmd/openearth-gryph/ # Binary entry point
├── go.mod # Depends on github.com/safedep/gryph
├── Makefile
├── LICENSE # Apache-2.0
└── README.md