A hands-off supply-chain watchdog for your dev machine. honey runs best-in-class security scanners on a schedule, unifies their results into one verdict, and (optionally) has Claude DM you a triage write-up each day.
honey doesn't detect anything itself — it orchestrates scanners, each a "lens" on a different risk:
| Lens | Answers | Upstream |
|---|---|---|
| bumblebee (core) | "Do I have a known-compromised package/extension?" | Perplexity |
| osv-scanner | "Do my dependencies have known CVEs?" (all ecosystems) | Google/OSV |
| govulncheck | "Do I actually call a vulnerable Go function?" | Go team |
| skillspector | "Is an installed AI agent skill behaving maliciously?" | NVIDIA |
bumblebee (the only required scanner) is a read-only inventory collector that flags on-disk package/extension/version metadata matching a known-compromised entry in a threat-intelligence catalog. The lenses are opt-in and inert until you install their tool. honey adds the update→scan→report loop, worst-wins verdict, scheduling, and reporting — all detection capability and threat data are the upstreams', above all Perplexity's bumblebee. See Acknowledgements.
Use honey three ways, smallest footprint first:
- Plain scanner —
./daily-cycle.sh, read the verdict. No Claude, no scheduler, no accounts. - Scheduled + desktop notification — a launchd/cron job alerts you on findings, with a deterministic report. No Claude required.
- Daily Claude routine — a Claude Code Local routine scans on a schedule and DMs an enriched triage write-up to Slack.
git clone https://github.com/<you>/honey && cd honey
./setup.sh # clones bumblebee, installs the binary + Go vuln lenses, verifies
./daily-cycle.sh # run a scan; read runs/latest/manifest.json for the verdictsetup.sh is idempotent and ends by running ./doctor.sh, which prints a
✓/✗ line for every dependency with exact fix-it commands for anything
missing. If a scan ever misbehaves, run ./doctor.sh first.
By default setup.sh also offers to install the two Go-based vulnerability
lenses (osv-scanner, govulncheck) —
you already have Go. The Python-based skillspector lens is pointed to, not
auto-installed. Pass HONEY_SETUP_INSTALL_LENSES=0 ./setup.sh to skip lenses.
Core: bash, git, jq, and Go
1.25+ (bumblebee needs 1.25+). macOS or Linux. setup.sh checks all of
these and tells you the install command for anything you're missing — it won't
guess your package manager. These also cover the two Go-based vuln lenses
(osv-scanner, govulncheck), which install via go install.
Per optional lens: only the skillspector lens adds a requirement —
Python 3.12+ (install it per
SkillSpector's README). The vuln
lenses need nothing beyond the core toolchain. ./doctor.sh shows which
lenses are active and how to enable inactive ones.
Why a bumblebee checkout and not just the binary? The threat-intel catalogs live in the repo's
threat_intel/directory, not inside the installed binary. honey needs both: the binary to scan, and a git checkout for the catalogs (which itgit pulls each run to stay current).setup.shhandles both.
git pull --ff-onlythe bumblebee checkout — freshthreat_intel/catalogs (they're updated upstream via PR).go install github.com/perplexityai/bumblebee/cmd/bumblebee@latest— fresh binary.bumblebee scan --profile deep --root $HOME --exposure-catalog <repo>/threat_intel --findings-only→ writes a timestamped run dir +manifest.json.daily-cycle.shexits0whenstatusisclean,1otherwise — so a scheduler or routine can branch on it.
A clean machine produces no findings — that's the "all clear". Results live
outside the bumblebee checkout and ~/go, so neither the git pull nor
the go install can delete them.
clean— scan completed, no matches.exposed— one or more catalog matches (seefindings.ndjson).incomplete— hit--max-duration; coverage partial, NOT all-clear.scan_error— scan failed; seecycle.log/diagnostics.ndjson.
bumblebee is honey's canonical lens (known-bad catalog matching). honey can
run additional lenses alongside it — independent scanners that cover a
different surface — and fold their results into one report. Each lens lives in
lenses/ as a small script; if its underlying tool isn't installed,
the lens is inert (a clean machine with no lens tools behaves exactly as
bumblebee-only). The cycle's overall verdict is the worst across bumblebee
and every active lens — a lens can escalate concern, never mask a bumblebee
finding.
Every lens is opt-in: honey never installs a lens's tool for you, and an
uninstalled lens is fully inert. Install the tool, and the lens activates on
the next run. ./doctor.sh shows which lenses are active.
| Lens | Tool | Covers | Install |
|---|---|---|---|
skillspector |
NVIDIA SkillSpector | AI agent skills (SKILL.md + scripts): prompt injection, data exfil, excessive agency, tool poisoning |
per its README (Python 3.12+) |
osv-scanner |
Google/OSV osv-scanner | Known vulns in lockfiles/manifests across all ecosystems (npm, pypi, cargo, go, …) | go install github.com/google/osv-scanner/cmd/osv-scanner@latest |
govulncheck |
Go vuln team govulncheck | Go vulns that your code actually calls (reachability-aware → far less noise) | go install golang.org/x/vuln/cmd/govulncheck@latest |
What each answers. bumblebee: "do I have a known-compromised package?" (exact catalog match). skillspector: "is this agent skill behaving maliciously?" (content analysis). osv-scanner: "do my dependencies have known CVEs?" (broad). govulncheck: "do I actually reach a vulnerable Go function?" (deep, low-noise). Together they cover reactive, proactive, breadth, and depth.
Where the vuln lenses scan. osv-scanner and govulncheck scan your project
directories, not all of $HOME. Set HONEY_PROJECT_ROOTS (colon-separated;
default ~/git:~/code:~/Developer:~/src) to control this. The skillspector
lens scans skill dirs under ~/.claude (override with HONEY_SKILL_ROOTS).
Declared deps, not vendored copies. osv-scanner walks into node_modules,
vendor/, .pnpm, and every nested .claude/worktrees/* — flagging the same
transitive dependency dozens of times across installed copies (one real run
produced 2,954 findings, 81% of them duplicated vendored packages; after
filtering, 402 real ones). By default honey excludes those paths and dedupes
identical package@version+advisory across lockfiles, so you see each real
vulnerable dependency once. Tune via HONEY_OSV_EXCLUDE_PATHS (set empty to
scan everything) and HONEY_OSV_NO_DEDUPE=1.
Go division of labor. osv-scanner and govulncheck overlap on Go. By
default osv-scanner defers Go stdlib advisories to govulncheck — otherwise
it flags every stdlib CVE for your go directive (often dozens, unreachable,
no CVSS). govulncheck reports only the stdlib vulns you actually call. Set
HONEY_OSV_INCLUDE_GO_STDLIB=1 to keep them in osv-scanner anyway.
Both vuln lenses run static DB lookups (osv-scanner → OSV.dev with
HONEY_OSV_OFFLINE=1 for local DBs; govulncheck → vuln.go.dev). skillspector
runs static (--no-llm) by default — honey's Claude routine is the semantic
layer; set HONEY_SKILLSPECTOR_LLM=1 to use SkillSpector's own LLM stage.
Two things can go stale — the vuln data and the scanner binaries — and honey treats them differently:
- Vuln data is always live. osv-scanner and govulncheck query their databases (OSV.dev, vuln.go.dev) on every scan, so a CVE published yesterday is caught today with no update step.
- Go lens binaries auto-update. Before scanning, honey
go install @latests osv-scanner and govulncheck (just as it does the bumblebee binary). SetHONEY_UPDATE_LENSES=0to disable. The update is non-fatal: if it fails (offline, etc.) the lens keeps its existing binary and scans anyway. A breaking upstream change surfaces asscan_error(visible), never a false clean. - skillspector is NOT auto-updated. Its detection patterns are bundled in the installed package, and its install method (pip/pipx/venv) is yours — honey never mutates Python environments. Re-install it periodically per its README to get new patterns.
If you use Claude Code and have the Slack connector enabled, you can have the whole thing run daily and DM you the analysis — no extra credentials, because a Local routine reuses your own Claude auth and connectors.
- In the Routines hub: New routine → Local.
- Paste the prompt from
routine-prompt.md(it runsdaily-cycle.shto scan,report.shfor the factual baseline, then DMs you an enriched write-up). SetHONEY_DIRin it to your checkout path. - Set the schedule (e.g. daily at noon).
On a clean run you get a one-line all-clear; on an exposed run you get
per-finding triage with drafted remediation, layering Claude's tailored
judgment on top of report.sh's deterministic facts. Because it's Local,
it can see your real filesystem (so the scan is meaningful) — a Remote cloud
routine cannot, and would always report clean.
Lenses in the routine — already handled. The prompt runs report.sh,
which covers bumblebee and every active lens, so the routine picks up
osv-scanner / govulncheck / skillspector automatically once their tools are
installed — no prompt change needed. Two things make this work without manual
config:
daily-cycle.shsets its own PATH (including~/go/binand~/.local/bin), so the lens tools resolve even though a Local routine starts with a bare environment.- The vuln lenses default to scanning
~/git:~/code:~/Developer:~/src. A Local routine does not inherit your shell's env vars, so if your projects live elsewhere, persist it inhoney.conf(addexport HONEY_PROJECT_ROOTS="${HONEY_PROJECT_ROOTS:-/my/projects:/other}") — every script, including the routine's, reads that. (Or add aSet HONEY_PROJECT_ROOTS=… before running the scripts.line to the prompt.) Same applies to a non-defaultBUMBLEBEE_REPO, whichsetup.shpersists for you — see Configuration.
To triage by hand in a chat instead ("triage the latest honey run"), see
triage-guide.md.
Don't use Claude or Slack? Schedule the scan with your OS and get a native desktop notification when something needs attention — no accounts, no connectors.
./install-schedule.sh # daily at noon (local); pass HH:MM to change
./install-schedule.sh status # is it scheduled?
./install-schedule.sh uninstall # remove it- macOS → installs a per-user launchd agent (
com.honey.bumblebee.notify) and notifies viaosascript(built in) orterminal-notifierif installed. - Linux → installs a user cron line and notifies via
notify-send(libnotify), provided a graphical session is active.
It runs notify-cycle.sh, which scans and — only when the
verdict is exposed / incomplete / scan_error — pops a notification
pointing at runs/latest/report.txt. Clean runs are silent.
You still get analysis without Claude. report.sh renders a
readable triage report straight from the structured findings — verdict,
coverage, and each match grouped by severity with its location, confidence,
and standard per-ecosystem remediation steps (npm/pip/go/bundler/composer/
brew/extension). The notify path writes it to runs/latest/report.txt
automatically; you can also run it anytime:
./report.sh # report on the latest run
./report.sh runs/<TS> # report on a specific runThe Claude routine produces richer, tailored prose and reasons about your
specific project layout, but the facts and standard fixes are all in
report.sh deterministically — so the no-Claude path is genuinely
actionable, not just a "something matched" ping.
macOS permissions: a scheduled job scanning all of
$HOMEmay need Full Disk Access (System Settings → Privacy & Security) to read protected directories, and notifications must be allowed for the running process. If a scheduled run sees fewer files than a manual one, Full Disk Access is why.
./doctor.sh # health check + which lenses are active
./run-scan.sh # just bumblebee (no cycle wrapper, no lenses)
./daily-cycle.sh # bumblebee + all active lenses + overall verdict
./report.sh # render the latest run (bumblebee + every lens)
# Narrow the scan or change the time budget:
BUMBLEBEE_SCAN_ROOT="$HOME/code" BUMBLEBEE_MAX_DURATION=20m ./run-scan.sh
# Point the vuln lenses at your projects:
HONEY_PROJECT_ROOTS="$HOME/work:$HOME/repos" ./daily-cycle.shAfter any run, runs/latest/manifest.json has the verdict and
runs/latest/findings.ndjson has any matches.
All settings are environment variables with working defaults — a default
install needs none of them. Set them inline (HONEY_PROJECT_ROOTS=… ./daily-cycle.sh)
or export them in your shell.
Persisting a setting (honey.conf). Env vars don't survive across shells,
and a Claude Local routine / cron job starts with a bare environment that
won't see them. For settings that must stick — most often a non-default
BUMBLEBEE_REPO — setup.sh writes a gitignored honey.conf at the repo
root that every script sources. If setup.sh finds an existing bumblebee
checkout at a non-default path, it offers to use it and persists the choice
there (rather than cloning a duplicate). Precedence is **env var > honey.conf
built-in default**, so an explicit env var still overrides the file for a one-off run. You can also hand-edit
honey.conf— oneexport VAR=…per line.
| Var | Default | Purpose |
|---|---|---|
HONEY |
the scripts' own directory | where runs are written |
BUMBLEBEE_REPO |
$HOME/git/bumblebee |
bumblebee checkout (for catalogs) |
BUMBLEBEE_SCAN_ROOT |
$HOME |
what bumblebee scans |
BUMBLEBEE_MAX_DURATION |
30m |
bumblebee scan time cap |
HONEY_PROJECT_ROOTS |
~/git:~/code:~/Developer:~/src |
where the vuln lenses (osv-scanner, govulncheck) look for projects |
HONEY_SKILL_ROOTS |
skill dirs under ~/.claude |
where the skillspector lens looks for agent skills |
HONEY_UPDATE_LENSES |
1 |
go install @latest the Go lens binaries (osv-scanner, govulncheck) before scanning; 0 to skip |
HONEY_OSV_OFFLINE |
0 |
osv-scanner: use local vuln DBs instead of OSV.dev |
HONEY_OSV_INCLUDE_GO_STDLIB |
0 |
osv-scanner: keep Go stdlib advisories (default defers them to govulncheck) |
HONEY_OSV_EXCLUDE_PATHS |
node_modules/vendor/.pnpm/testdata/fixtures/.claude/worktrees (ERE) |
osv-scanner: skip findings whose source path matches — keeps the scan on declared manifests, not installed/vendored copies. Set empty to scan everything. |
HONEY_OSV_NO_DEDUPE |
0 |
osv-scanner: 1 keeps one finding per path; default collapses identical package@version+advisory across paths into one (+N more) |
HONEY_SKILLSPECTOR_LLM |
0 |
skillspector: enable its own LLM stage (default static --no-llm) |
honey/
├── setup.sh # one-command setup: bumblebee + Go vuln lenses, then verify
├── doctor.sh # dependency health check with fix-it hints
├── lib/preflight.sh # shared dependency checks (sourced by the above)
├── run-scan.sh # update repo+binary, deep-scan, write a run + manifest
├── daily-cycle.sh # one cycle: run-scan, exit 0=clean / 1=needs attention
├── notify-cycle.sh # scan + native desktop notification (no-Claude path)
├── report.sh # deterministic triage report (bumblebee + all lenses; no AI)
├── install-schedule.sh # schedule notify-cycle via launchd (macOS) / cron (Linux)
├── lenses/ # optional additional scanners, each self-skips if its tool is absent
│ ├── skillspector.sh # AI agent skills (NVIDIA SkillSpector)
│ ├── osv-scanner.sh # multi-ecosystem lockfile vulns (Google/OSV)
│ └── govulncheck.sh # Go reachability-aware vulns (Go vuln team)
├── routine-prompt.md # prompt for the Claude Local routine (scheduled path)
├── triage-guide.md # guide for triaging a run by hand in a chat
├── runs/<TS>/ # one timestamped run per scan (gitignored — host inventory)
│ ├── manifest.json # bumblebee verdict + metadata
│ ├── findings.ndjson # bumblebee finding records
│ └── lens-<name>.json # each active lens's normalized findings
└── latest -> runs/… # symlink to the most recent run (gitignored)
runs/, latest, and *.log are gitignored — they contain an inventory
of your machine and should never be published.
| Symptom | Fix |
|---|---|
| Anything unexpected | ./doctor.sh — it pinpoints the cause |
command not found: bumblebee |
Go's bin dir isn't on PATH; doctor.sh prints the export PATH=… line to add |
| Scan finds nothing even when it should | Usually a missing/stale catalog checkout — ./setup.sh re-clones/updates it |
jq not found |
brew install jq (macOS) / sudo apt-get install jq (Debian/Ubuntu) |
go build fails |
Need Go 1.25+; upgrade from https://go.dev/dl/ |
Status incomplete |
Scan hit the time cap; raise BUMBLEBEE_MAX_DURATION or narrow BUMBLEBEE_SCAN_ROOT |
honey is a thin wrapper. The actual scanning engine and all threat intelligence are the work of Perplexity:
- bumblebee — the read-only inventory collector and exposure scanner honey drives. Every detection, every ecosystem parser, the scan profiles, and the record schema are bumblebee's.
- threat_intel catalogs — the maintained exposure catalogs of recent supply-chain campaigns that honey matches against, assembled by Perplexity from public threat-intelligence reporting and updated via PRs.
Both are published by Perplexity under the Apache License 2.0. honey adds only the update→scan→report loop, scheduling, and reporting around them. If honey is useful to you, the credit for the hard part belongs upstream — please star and follow bumblebee.
The optional lenses wrap further upstream tools — honey installs none of them automatically and redistributes no code; each lens is inert unless you install its tool yourself:
- NVIDIA SkillSpector (Apache 2.0)
— all AI-agent-skill vulnerability detection in the
skillspectorlens. - osv-scanner by Google / the
OSV-Scanner authors (Apache 2.0) — all multi-ecosystem lockfile vulnerability
detection in the
osv-scannerlens. - govulncheck by the Go Authors (BSD-style)
— all Go reachability-aware vulnerability detection in the
govulnchecklens.
All detection capability and vulnerability data are the work of these upstream projects. See NOTICE for full attribution.
honey is licensed under Apache 2.0. It depends on, but does not include, bumblebee and its threat_intel catalogs, which are independently licensed by Perplexity under Apache 2.0. See NOTICE for attribution.