eyebrow

module
v0.4.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 5, 2026 License: MIT

README

eyebrow

eyebrow

ci

 

Supply-chain integrity for AI coding tools. A single static binary that discovers every skill, MCP server, plugin, hook, and rule installed across your AI coding tools, hashes them into a lockfile, statically scans them, and detects post-audit modification — "rug pulls" — before they bite.

Status: Component 1 (the read-only scan/verify wedge) and Component 2 (the runtime MCP firewall — wrap, sandbox, egress proxy) are implemented. Component 3 ships a local, embedded dashboard with usage telemetry, fleet blast-radius, policy conformance, and an opt-in reputation signal; the hosted team API is designed but not yet built. See docs/architecture/ARCHITECTURE.md.

Why

Skills, MCP servers, and hooks run with your privileges and can change after you audit them. eyebrow gives you a committable lockfile (eyebrowlock.json) of exactly what's installed and what it does, and tells you the moment any of it changes.

Install

Homebrew (macOS / Linux):

brew install alexverify/tap/eyebrow

From source — the strongest trust path; needs only Go 1.25+:

go install github.com/alexverify/eyebrow/cmd/eyebrow@latest
# or
git clone https://github.com/alexverify/eyebrow && cd eyebrow && make install

Shell installer (no Homebrew; downloads a checksum-verified binary):

curl -fsSL https://raw.githubusercontent.com/alexverify/eyebrow/main/install.sh | sh

Manual: grab a static binary from the releases page.

Verify what you downloaded

Release binaries are unsigned by choice (no paid Apple/Windows signing accounts — the Homebrew cask clears macOS quarantine on install). Every release ships free, cryptographic verification instead:

# checksum (also done automatically by install.sh)
shasum -a 256 -c --ignore-missing checksums.txt

# build provenance: proves this exact file came from this repo's release workflow
gh attestation verify eyebrow_*.tar.gz -R alexverify/eyebrow

Prefer proof over trust? Build from source (above).

Quickstart

eyebrow scan and digest in a project with a risky skill

make install          # build + install `eyebrow` onto your PATH (zero external deps)
                      #   installs to your Go bin; if `eyebrow` isn't found, add that
                      #   dir to PATH (the command prints the exact line). Prefer not
                      #   to install? `make build` produces ./bin/eyebrow instead.

# From a project that uses Claude Code (has .mcp.json and/or .claude/skills):
eyebrow scan            # discover, hash, analyze → writes eyebrowlock.json
eyebrow doctor          # post-install health check: tools found, lockfile, keys
eyebrow list            # pretty inventory across tools
eyebrow verify          # recompute & diff vs the lockfile (rug-pull check)
eyebrow verify --ci     # strict: apply the policy gate (see Policy below)
eyebrow diff            # informational: what changed since the lockfile
eyebrow approve <id>    # mark an artifact approved in the lockfile
eyebrow sign            # sign the lockfile with your local ed25519 key
eyebrow key show        # print your public key (share it with your team)
eyebrow key trust <k>   # trust a teammate's public key
eyebrow wrap            # audit every MCP tool call via the stdio shim
eyebrow wrap --status   # what's wrapped + the real underlying commands
eyebrow unwrap          # restore the original MCP config
eyebrow install-hooks   # add usage hooks so skills/subagents get telemetry too
eyebrow dashboard       # local web dashboard: inventory, drift, usage, fleet
eyebrow fleet export    # write this machine's content-free snapshot to .eyebrow/fleet
eyebrow fleet           # team blast-radius + policy conformance from snapshots
eyebrow fleet verify    # CI gate: exit 1 if any machine is out of policy
eyebrow serve           # run the self-hostable team control plane (opt-in)
eyebrow fleet push      # submit this machine's snapshot to a control plane (--server)
eyebrow audit push      # upload this machine's audit events (--server)
eyebrow alerts          # team alerts: drift, quarantine, blocked egress (--server)

Not a Go-bin-on-PATH person? sudo make install PREFIX=/usr/local installs to /usr/local/bin. For one-off runs without installing, make run ARGS="scan".

Exit codes (stable for CI): 0 clean · 1 drift / findings over threshold · 2 usage error · 3 internal error.

The full solo and team workflows — policy, approvals, signing, trusted keys, CI — are walked through in docs/usage.md.

Policy (CI gating)

verify --ci applies an optional eyebrow.policy.json (commit it next to the lockfile). Absent a file, the default gate fails on any new high/critical finding. Example:

{
  "failOnSeverity": "high",          // gate on new findings at/above this severity
  "ignoreRules": ["EXEC-PRIMITIVE"], // accepted false positives, suppressed
  "blockPublishers": ["giftshop.club"], // fail any artifact from these sources
  "blockArtifacts": ["sketchy-skill"],  // fail any artifact by name substring
  "allowPublishers": ["github.com/acme/"], // if set, fail anything not from here
  "requireApproval": true,           // fail any artifact not `eyebrow approve`d
  "requireSignedApproval": true,     // fail unless each approval is signed by a trusted key
  "requireSignature": true,          // fail unless the lockfile is validly signed
  "mcp": {                           // runtime tool rules, enforced live by `wrap`
    "servers": { "github": { "denyTools": ["delete_*"] } }
  }
}

With requireSignature, the lockfile signature is checked against a trusted-keys registry: eyebrow.trustedkeys committed next to the lockfile (one base64 ed25519 public key per line, optional label, # comments) merged with your personal ~/.eyebrow/trusted_keys. Each teammate shares their key with eyebrow key show and registers others with eyebrow key trust <key> --name alice --file eyebrow.trustedkeys. When no registry declares any key, your own local key is trusted, so the single-user flow needs no setup; once a registry exists it is authoritative — local verify --ci behaves exactly like CI.

A committed lockfile + policy + trusted keys + the verify --ci exit code give a small team "only approved, unmodified, clean, signed-by-us artifacts run here" with no infrastructure.

GitHub Action
steps:
  - uses: actions/checkout@v4
  - uses: alexverify/eyebrow/action@v0.3.0

One tag pins the action and the checksum-verified binary it installs; see action/README.md for inputs.

Requirements

The binary itself has no runtime dependencies. To pin and hash remote sources during a scan, eyebrow shells out to the relevant tool:

  • npm — to resolve npx/npm MCP servers to an exact version + integrity and fetch the package code.
  • git — to resolve git sources to a commit SHA.

These are optional: if npm/git aren't on PATH, that source simply can't be pinned and is recorded as a finding instead — the scan still completes. Local paths, inline content, and remote-URL certificate pinning need nothing extra.

What it detects today

  • Drift / rug pulls — any artifact whose content hash, pinned version, npm integrity, or remote TLS certificate changed since you locked it.
  • High-signal static findings, mapped to the OWASP Agentic Skills Top 10: remote-exec pipes (curl … | sh), obfuscation (eval/atob), sensitive-path reads (~/.ssh, ~/.aws, .env), exec primitives, npm install hooks, and prompt-injection / consent-bypass language in skills and rules.
  • Unverifiable sources — unpinned or remote sources are flagged rather than silently trusted.

Architecture

Pragmatic hexagonal (ports & adapters) in idiomatic Go: a pure, exhaustively tested domain core (the hashing and drift logic), application use-cases that depend only on interfaces, and swappable adapters for every external surface. The core leans on the Go standard library with a single, deliberate exception — a TOML parser for Codex configs — because a supply-chain tool should keep its own dependency surface auditable.

Read docs/architecture/ARCHITECTURE.md for the package map, data flow, testing strategy, and how to extend it (adding a tool, resolver, or analyzer is a localized change behind one interface). The key design choices and their trade-offs are written up in docs/architecture/decisions.md.

Development

make build    # build the binary
make test     # run all tests
make check    # gofmt + vet + tests (the local CI gate)
make help     # list all tasks

Requires Go 1.25+. See CONTRIBUTING.md.

Roadmap

Component What Status
1 — scan/verify/lockfile Read-only inventory, hashing, analysis, drift, signing/trust, CI Action implemented (Claude Code, Claude Desktop, Cursor, Gemini, OpenCode, Codex, Windsurf, Copilot CLI, VS Code, Zed, Kiro)
2 — wrap MCP interposition supervisor, OS sandbox, egress proxy + redaction implemented — shim with audit log, live tool policy, egress proxy + secret redaction, OS sandbox (Seatbelt/bwrap)
3 — control plane Policy API, audit log, approval workflow, dashboard in progress — local dashboard (embedded Next.js UI, eyebrow dashboard) shipped: trust verdicts, capability & file-manifest drift diff (with line-level diffs when a baseline is captured), usage telemetry (MCP tool calls + skill/subagent activation hooks) + dormant-then-active detection, per-artifact timeline, reachability-aware findings, fleet blast-radius / inventory heatmap / policy conformance with an enforced CI gate (eyebrow fleet / fleet verify), and an opt-in hash-only reputation signal; a self-hostable team server (eyebrow serve) ingests snapshots and audit events, serves the same aggregated blast-radius, org policy / trusted-keys pull, a hosted CI gate (fleet verify --server), team alerts (eyebrow alerts), a live hash-only reputation lookup (eyebrow reputation), and the local dashboard's Fleet/Alerts tabs on hosted data (eyebrow dashboard --server) — slices 4a–4f. A centrally-hosted multi-user UI with SSO remains designed. What leaves a machine is specified in docs/privacy.md

License

MIT.

Directories

Path Synopsis
cmd
eyebrow command
Command eyebrow is the single static binary entrypoint.
Command eyebrow is the single static binary entrypoint.
internal
adapters/analyze
Package analyze runs static analysis over resolved artifact code.
Package analyze runs static analysis over resolved artifact code.
adapters/auditlog
Package auditlog appends audit events as JSONL, one file per UTC day.
Package auditlog appends audit events as JSONL, one file per UTC day.
adapters/cpstore
Package cpstore is a file-backed store for the control-plane server.
Package cpstore is a file-backed store for the control-plane server.
adapters/discover
Package discover walks tool configurations and normalizes every skill, MCP server, plugin, hook, rule, and context file into the domain Artifact model.
Package discover walks tool configurations and normalizes every skill, MCP server, plugin, hook, rule, and context file into the domain Artifact model.
adapters/fleetstore
Package fleetstore reads and writes fleet snapshots as one JSON file per owner under a shared directory (the "git is the backend" path, e.g.
Package fleetstore reads and writes fleet snapshots as one JSON file per owner under a shared directory (the "git is the backend" path, e.g.
adapters/hash
Package hash adapts the pure domain digest algorithm to the filesystem.
Package hash adapts the pure domain digest algorithm to the filesystem.
adapters/historystore
Package historystore appends and reads counts-only posture snapshots to a local JSONL file (~/.eyebrow/history.jsonl), the data behind the posture trend.
Package historystore appends and reads counts-only posture snapshots to a local JSONL file (~/.eyebrow/history.jsonl), the data behind the posture trend.
adapters/hookconfig
Package hookconfig installs (and removes) the host-tool hooks that feed eyebrow's usage telemetry.
Package hookconfig installs (and removes) the host-tool hooks that feed eyebrow's usage telemetry.
adapters/lockstore
Package lockstore reads and writes eyebrowlock.json.
Package lockstore reads and writes eyebrowlock.json.
adapters/mcpconfig
Package mcpconfig rewrites MCP server configs (Claude Code's .mcp.json) so stdio servers launch through the eyebrow shim, and restores them.
Package mcpconfig rewrites MCP server configs (Claude Code's .mcp.json) so stdio servers launch through the eyebrow shim, and restores them.
adapters/notify
Package notify posts short digests to a chatops webhook.
Package notify posts short digests to a chatops webhook.
adapters/parse
Package parse normalizes the heterogeneous config formats used by AI coding tools into Go values: JSON, JSONC (Claude/Cursor/VS Code-style, with comments and trailing commas), and TOML (Codex).
Package parse normalizes the heterogeneous config formats used by AI coding tools into Go values: JSON, JSONC (Claude/Cursor/VS Code-style, with comments and trailing commas), and TOML (Codex).
adapters/policystore
Package policystore loads a policy file (eyebrow.policy.json) from disk.
Package policystore loads a policy file (eyebrow.policy.json) from disk.
adapters/report
Package report renders scan, verify, and list results.
Package report renders scan, verify, and list results.
adapters/repstore
Package repstore loads an opt-in community reputation corpus (theme H3) from a local JSON file: a content-hash → Signal map the user chose to trust.
Package repstore loads an opt-in community reputation corpus (theme H3) from a local JSON file: a content-hash → Signal map the user chose to trust.
adapters/resolve
Package resolve turns a Source declaration into concrete, pinned, content-addressable code (or an integrity anchor for sources that cannot be hashed locally).
Package resolve turns a Source declaration into concrete, pinned, content-addressable code (or an integrity anchor for sources that cannot be hashed locally).
adapters/sbom
Package sbom renders a lockfile as a CycloneDX 1.6 SBOM: one component per discovered artifact (skill, MCP server, plugin, …) and one vulnerability per static-analysis finding.
Package sbom renders a lockfile as a CycloneDX 1.6 SBOM: one component per discovered artifact (skill, MCP server, plugin, …) and one vulnerability per static-analysis finding.
adapters/sign
Package sign implements detached ed25519 signatures over canonical bytes, satisfying ports.Signer.
Package sign implements detached ed25519 signatures over canonical bytes, satisfying ports.Signer.
adapters/snapshotstore
Package snapshotstore is a content-addressed blob store for the *approved* bytes of an artifact's files.
Package snapshotstore is a content-addressed blob store for the *approved* bytes of an artifact's files.
app/apptest
Package apptest provides in-memory fakes implementing the application ports.
Package apptest provides in-memory fakes implementing the application ports.
app/ports
Package ports declares the interfaces (the "ports" of the hexagon) that the application services depend on.
Package ports declares the interfaces (the "ports" of the hexagon) that the application services depend on.
app/scan
Package scan implements the `scan` use case: discover artifacts, resolve and pin their sources, hash them, run static analysis, and assemble a lockfile.
Package scan implements the `scan` use case: discover artifacts, resolve and pin their sources, hash them, run static analysis, and assemble a lockfile.
app/shim
Package shim implements the MCP interposition relay — the heart of `eyebrow wrap` (Component 2).
Package shim implements the MCP interposition relay — the heart of `eyebrow wrap` (Component 2).
app/verify
Package verify implements the `verify` use case — the rug-pull detector.
Package verify implements the `verify` use case — the rug-pull detector.
buildinfo
Package buildinfo exposes build-time metadata stamped via -ldflags.
Package buildinfo exposes build-time metadata stamped via -ldflags.
cli
Package cli is the driving adapter: it parses arguments, wires the concrete adapters into the application services (the composition root), and maps outcomes to process exit codes.
Package cli is the driving adapter: it parses arguments, wires the concrete adapters into the application services (the composition root), and maps outcomes to process exit codes.
client
Package client is the CLI-side HTTP client for the self-hostable team control plane (Component 3b).
Package client is the CLI-side HTTP client for the self-hostable team control plane (Component 3b).
controlplane
Package controlplane is the self-hostable team server (Component 3b, slice 4a): it ingests content-free fleet snapshots from each machine and aggregates them with the exact pure functions the local dashboard uses (fleet.Aggregate), so a team beyond the "git is the backend" scale gets the same blast-radius view over the wire.
Package controlplane is the self-hostable team server (Component 3b, slice 4a): it ingests content-free fleet snapshots from each machine and aggregates them with the exact pure functions the local dashboard uses (fleet.Aggregate), so a team beyond the "git is the backend" scale gets the same blast-radius view over the wire.
dashboard
Package dashboard serves a local, read-only web view of what eyebrow sees on this machine: the inventory, drift against the lockfile, findings, and the MCP shim's audit timeline.
Package dashboard serves a local, read-only web view of what eyebrow sees on this machine: the inventory, drift against the lockfile, findings, and the MCP shim's audit timeline.
dashboard/demodata
Package demodata builds the in-memory dataset behind EYEBROW_DEMO=1: a fictional-but-realistic environment that lights up every dashboard tab so the product can be demoed on any machine without scanning anything real.
Package demodata builds the in-memory dataset behind EYEBROW_DEMO=1: a fictional-but-realistic environment that lights up every dashboard tab so the product can be demoed on any machine without scanning anything real.
domain/advisory
Package advisory matches discovered artifacts against a curated, offline feed of known-malicious skills and MCP servers.
Package advisory matches discovered artifacts against a curated, offline feed of known-malicious skills and MCP servers.
domain/alert
Package alert derives team-level alerts from what the control plane actually holds: the aggregated fleet report (which artifacts drifted, are quarantined, or woke as sleepers, and on how many machines) and the ingested audit events (which wrapped servers were blocked from egress or had a tool call denied).
Package alert derives team-level alerts from what the control plane actually holds: the aggregated fleet report (which artifacts drifted, are quarantined, or woke as sleepers, and on how many machines) and the ingested audit events (which wrapped servers were blocked from egress or had a tool call denied).
domain/artifact
Package artifact defines the normalized internal model for every "thing" eyebrow discovers across AI coding tools: skills, MCP servers, plugins, subagents, hooks, rules, and context files.
Package artifact defines the normalized internal model for every "thing" eyebrow discovers across AI coding tools: skills, MCP servers, plugins, subagents, hooks, rules, and context files.
domain/audit
Package audit models the events the MCP shim records: one line per tool call, plus session lifecycle markers.
Package audit models the events the MCP shim records: one line per tool call, plus session lifecycle markers.
domain/digest
Package digest implements eyebrow's canonical, content-addressable integrity primitive.
Package digest implements eyebrow's canonical, content-addressable integrity primitive.
domain/doctor
Package doctor models the result of an environment self-check: a list of named checks, each with a status and a one-line detail, plus a summary.
Package doctor models the result of an environment self-check: a list of named checks, each with a status and a one-line detail, plus a summary.
domain/finding
Package finding models the output of static analysis over an artifact.
Package finding models the output of static analysis over an artifact.
domain/fleet
Package fleet aggregates per-developer inventory snapshots into a team-wide blast-radius view (theme G1): "crypto-price-feed just drifted — 3 of 8 engineers have it installed." It answers "who is exposed" the moment an advisory lands, which is impossible to see from a single laptop.
Package fleet aggregates per-developer inventory snapshots into a team-wide blast-radius view (theme G1): "crypto-price-feed just drifted — 3 of 8 engineers have it installed." It answers "who is exposed" the moment an advisory lands, which is impossible to see from a single laptop.
domain/jsonrpc
Package jsonrpc models the slice of JSON-RPC 2.0 the MCP shim inspects.
Package jsonrpc models the slice of JSON-RPC 2.0 the MCP shim inspects.
domain/lockfile
Package lockfile defines the eyebrowlock.json model and the drift-detection logic that powers `eyebrow verify` — the rug-pull detector.
Package lockfile defines the eyebrowlock.json model and the drift-detection logic that powers `eyebrow verify` — the rug-pull detector.
domain/policy
Package policy defines the rules a team can enforce over an inventory, and the pure evaluation that turns a locked + current snapshot into a pass/fail decision.
Package policy defines the rules a team can enforce over an inventory, and the pure evaluation that turns a locked + current snapshot into a pass/fail decision.
domain/posture
Package posture summarizes an inventory into a single trust verdict and the counts behind it — the onboarding "first verdict" and the data point a trend is built from.
Package posture summarizes an inventory into a single trust verdict and the counts behind it — the onboarding "first verdict" and the data point a trend is built from.
domain/provenance
Package provenance grades how verifiable an artifact's origin is, the agent-specific analog of a SLSA level.
Package provenance grades how verifiable an artifact's origin is, the agent-specific analog of a SLSA level.
domain/reach
Package reach classifies whether a finding's file is on a path the artifact actually runs (theme H2).
Package reach classifies whether a finding's file is on a path the artifact actually runs (theme H2).
domain/registry
Package registry holds the pure, IO-free rules for assessing an application published to a remote catalog (an app store listing).
Package registry holds the pure, IO-free rules for assessing an application published to a remote catalog (an app store listing).
domain/reputation
Package reputation models an opt-in, privacy-preserving community trust signal (theme H3): "this exact artifact hash is trusted by N other eyebrow users, first seen 2026-04." It is the one signal a purely local tool cannot build alone — a network effect that sharpens as adoption grows.
Package reputation models an opt-in, privacy-preserving community trust signal (theme H3): "this exact artifact hash is trusted by N other eyebrow users, first seen 2026-04." It is the one signal a purely local tool cannot build alone — a network effect that sharpens as adoption grows.
domain/risk
Package risk fuses an artifact's static severity with its runtime usage (theme F3): a finding on code that has actually run is more urgent than the same finding on code that has only ever sat on disk.
Package risk fuses an artifact's static severity with its runtime usage (theme F3): a finding on code that has actually run is more urgent than the same finding on code that has only ever sat on disk.
domain/secrets
Package secrets detects and redacts well-known credential shapes in byte streams.
Package secrets detects and redacts well-known credential shapes in byte streams.
domain/textdiff
Package textdiff is a small, dependency-free line differ.
Package textdiff is a small, dependency-free line differ.
domain/timeline
Package timeline assembles the per-artifact event ribbon (theme F4): the unified "what happened, when" stream a reviewer wants in an incident — installed → approved → first invoked → drifted → last invoked — ordered in time.
Package timeline assembles the per-artifact event ribbon (theme F4): the unified "what happened, when" stream a reviewer wants in an incident — installed → approved → first invoked → drifted → last invoked — ordered in time.
domain/toolsurface
Package toolsurface models an MCP server's advertised tool surface — the tool names, descriptions, and input schemas a tools/list response carries.
Package toolsurface models an MCP server's advertised tool surface — the tool names, descriptions, and input schemas a tools/list response carries.
domain/trust
Package trust turns the facts eyebrow already knows about an artifact — its findings, how it drifted, whether its source is pinned, what it can do, and whether a trusted key approved it — into a single action-mapped Verdict plus a transparent, hand-recomputable breakdown.
Package trust turns the facts eyebrow already knows about an artifact — its findings, how it drifted, whether its source is pinned, what it can do, and whether a trusted key approved it — into a single action-mapped Verdict plus a transparent, hand-recomputable breakdown.
domain/usage
Package usage turns the runtime audit log into a per-artifact picture of what actually ran, and when (theme F).
Package usage turns the runtime audit log into a per-artifact picture of what actually ran, and when (theme F).
platform/run
Package run abstracts external command execution so adapters that shell out (npm, git) can be unit-tested with a scripted fake instead of a real process.
Package run abstracts external command execution so adapters that shell out (npm, git) can be unit-tested with a scripted fake instead of a real process.
proxy
Package proxy implements Component 2's egress proxy: a local forward proxy the shim points wrapped MCP servers at via HTTP(S)_PROXY.
Package proxy implements Component 2's egress proxy: a local forward proxy the shim points wrapped MCP servers at via HTTP(S)_PROXY.
sandbox
Package sandbox confines a wrapped MCP server to an allowed workspace and forces its network through the egress proxy, using OS primitives (macOS Seatbelt, Linux bubblewrap).
Package sandbox confines a wrapped MCP server to an allowed workspace and forces its network through the egress proxy, using OS primitives (macOS Seatbelt, Linux bubblewrap).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL