The operator's seat for the Willow AI fleet — loopback-only served page + MCP server
Drift inferred · capture-to-capture
No drift recorded — single capability capture; advisories appear once its surface changes.
transport stdio · streamable-http · http counts 23 tools · 0 res
· 0 prompts
permission surface via code analysis
tools
-
grove_ack
-
grove_agents
-
grove_bus_delete
-
grove_bus_receive
-
grove_bus_send
-
grove_create_channel
-
grove_flag
-
grove_flagged
-
grove_fleet_status
-
grove_get_history
-
grove_get_identity
-
grove_get_thread
-
grove_heartbeat
-
grove_human_required
-
grove_inbox
-
grove_list_channels
-
grove_mentions
-
grove_reply
-
grove_search
-
grove_send_message
-
grove_unflag
-
grove_watch
-
grove_watch_all
prompt-surface
shipped agent-instruction files + hidden-content / dangerous-code findings —
quoted from the analyzed source
analyzed v0.9.0 · analyzer v33 · 2w ago
skills & prompt files 1
- agent-rules willows_grove-0.9.0/CLAUDE.md
danger signals1
- suspicious endpoint grove.example.ngrok.app willows_grove-0.9.0/tests/test_transport_security.py :45
"https://grove.example.ngrok.app",
evidence-backed
findings quoted directly from the published source artifact — not inferred
code files: 129
filesystem 57
- fs willows_grove-0.9.0/bridge/__main__.py :33
from pathlib import Path - fs willows_grove-0.9.0/bridge/app.py :25
from pathlib import Path - fs willows_grove-0.9.0/bridge/store.py :3
from pathlib import Path - fs willows_grove-0.9.0/grove/envelope_reader.py :43
from pathlib import Path - fs willows_grove-0.9.0/grove/mcp_auth.py :41
from pathlib import Path - fs willows_grove-0.9.0/grove/mcp_local.py :47
from pathlib import Path - fs willows_grove-0.9.0/grove/nestor_client.py :31
import shutil - fs willows_grove-0.9.0/grove/persona_roster.py :35
from pathlib import Path - fs willows_grove-0.9.0/grove/resident_watcher.py :53
from pathlib import Path - fs willows_grove-0.9.0/grove/seed_reader.py :37
from pathlib import Path - fs willows_grove-0.9.0/grove_serve.py :27
from pathlib import Path - fs (weak) willows_grove-0.9.0/scripts/check_docs_drift.py :33
from pathlib import Path
show 28 more
- fs (weak) willows_grove-0.9.0/scripts/check_persona_provenance.py :29
from pathlib import Path - fs (weak) willows_grove-0.9.0/scripts/check_ratification.py :38
from pathlib import Path - fs (weak) willows_grove-0.9.0/scripts/mcp_entry_toggle.py :21
with open(path) as f: - fs (weak) willows_grove-0.9.0/tests/e2e_ollama/conftest.py :50
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/e2e_ollama/test_watcher_ollama_e2e.py :34
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/e2e_willow_mcp/conftest.py :24
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_ci_hashfiles_guards_removed.py :42
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_ci_playwright_install_command.py :27
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_ci_security_grep_docstring_matches_pattern.py :21
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_claude_md_honesty.py :29
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_docs_drift_check.py :18
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_docs_no_dead_envvar_references.py :13
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_e2e_ollama_ci_fails_loud.py :36
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_envelope_reader.py :14
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_frank_ledger_error_surfaces.py :27
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_grove_approval_page.py :21
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_grove_chat_render.py :16
import pathlib - fs (weak) willows_grove-0.9.0/tests/test_grove_db_timeouts.py :25
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_grove_lens_switch.py :51
with open(component, "r", encoding="utf-8") as fh: - fs (weak) willows_grove-0.9.0/tests/test_grove_serve_env_validation.py :30
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_grove_serve_envelopes.py :23
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_grove_serve_personas.py :29
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_mcp_auth.py :12
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_mcp_local_oauth_hardening.py :28
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_mcp_serve_oauth_flow.py :12
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_nestor_client.py :7
import shutil - fs (weak) willows_grove-0.9.0/tests/test_panel_wiring.py :40
from pathlib import Path - fs (weak) willows_grove-0.9.0/tests/test_pending_ttl_hard_ceiling.py :12
from pathlib import Path
17 more not shown — this panel samples each group; the count above is the real total.
shell / exec 11
- shell willows_grove-0.9.0/grove/mcp_local.py :1194
import subprocess - shell willows_grove-0.9.0/grove/nestor_client.py :32
import subprocess - shell willows_grove-0.9.0/grove_serve.py :24
import subprocess - shell (weak) willows_grove-0.9.0/scripts/check_persona_provenance.py :27
import subprocess - shell (weak) willows_grove-0.9.0/tests/test_ci_hashfiles_guards_removed.py :40
import subprocess - shell (weak) willows_grove-0.9.0/tests/test_ci_security_grep_docstring_matches_pattern.py :38
# - os.system( — arbitrary shell invocation - shell (weak) willows_grove-0.9.0/tests/test_docs_drift_check.py :16
import subprocess - shell (weak) willows_grove-0.9.0/tests/test_grove_serve_env_validation.py :28
import subprocess - shell (weak) willows_grove-0.9.0/tests/test_persona_provenance_check.py :14
import subprocess - shell (weak) willows_grove-0.9.0/tests/test_ratification_check.py :9
import subprocess - shell (weak) willows_grove-0.9.0/tests/test_refusal_summon_shape.py :34
import subprocess
network 25
- net willows_grove-0.9.0/bridge/app.py :24
import socket - net willows_grove-0.9.0/bridge/matrix.py :4
from aiohttp import web, ClientSession, ClientTimeout - net willows_grove-0.9.0/grove/journal_reader.py :40
import urllib.error - net willows_grove-0.9.0/grove/journal_writer.py :34
import urllib.error - net willows_grove-0.9.0/grove/mcp_local.py :42
import socket - net willows_grove-0.9.0/grove/resident_watcher.py :51
import urllib.error - net (weak) willows_grove-0.9.0/tests/e2e_ollama/conftest.py :48
import urllib.error - net (weak) willows_grove-0.9.0/tests/e2e_ollama/test_watcher_ollama_readiness.py :21
import urllib.error - net (weak) willows_grove-0.9.0/tests/e2e_willow_mcp/conftest.py :18
import socket - net (weak) willows_grove-0.9.0/tests/e2e_willow_mcp/test_watcher_chat_readback_flow.py :54
# ``urllib.request.urlopen`` at the module level would also bleed - net (weak) willows_grove-0.9.0/tests/test_grove_approval_page.py :22
from urllib.parse import parse_qs, urlparse - net (weak) willows_grove-0.9.0/tests/test_grove_serve.py :14
import socket
show 13 more
- net (weak) willows_grove-0.9.0/tests/test_grove_serve_dispatch.py :15
import socket - net (weak) willows_grove-0.9.0/tests/test_grove_serve_envelopes.py :15
import socket - net (weak) willows_grove-0.9.0/tests/test_grove_serve_journal.py :16
import socket - net (weak) willows_grove-0.9.0/tests/test_grove_serve_journal_read.py :15
import socket - net (weak) willows_grove-0.9.0/tests/test_grove_serve_nestor.py :18
import socket - net (weak) willows_grove-0.9.0/tests/test_grove_serve_personas.py :21
import socket - net (weak) willows_grove-0.9.0/tests/test_grove_serve_seed.py :12
import socket - net (weak) willows_grove-0.9.0/tests/test_journal_reader.py :88
journal_reader.urllib.request, "urlopen", - net (weak) willows_grove-0.9.0/tests/test_journal_writer.py :109
journal_writer.urllib.request, "urlopen", _fake_urlopen - net (weak) willows_grove-0.9.0/tests/test_mcp_serve_oauth_flow.py :13
from urllib.parse import parse_qs, urlparse - net (weak) willows_grove-0.9.0/tests/test_panel_wiring.py :32
import socket - net (weak) willows_grove-0.9.0/tests/test_persona_roster_unreachable.py :28
import socket - net (weak) willows_grove-0.9.0/tests/test_seed_canon_content.py :23
import socket
database 14
- db willows_grove-0.9.0/bridge/store.py :2
import sqlite3 - db willows_grove-0.9.0/grove/kart_reader.py :232
import psycopg2 # local import — kart_reader is a leaf module - db willows_grove-0.9.0/grove/mcp_local.py :79
import psycopg2 - db willows_grove-0.9.0/grove/resident_watcher.py :315
import psycopg2 # type: ignore - db willows_grove-0.9.0/grove_db.py :107
import psycopg2.pool - db willows_grove-0.9.0/grove_reader.py :65
import psycopg2 - db (weak) willows_grove-0.9.0/tests/e2e_ollama/conftest.py :334
import psycopg2 # type: ignore - db (weak) willows_grove-0.9.0/tests/e2e_ollama/test_watcher_ollama_e2e.py :63
import psycopg2 # type: ignore - db (weak) willows_grove-0.9.0/tests/test_frank_ledger_error_surfaces.py :46
import psycopg2.errors - db (weak) willows_grove-0.9.0/tests/test_grove_db_cursor_unreachable.py :35
import psycopg2 # noqa: E402 - db (weak) willows_grove-0.9.0/tests/test_grove_db_timeouts.py :72
import psycopg2 - db (weak) willows_grove-0.9.0/tests/test_grove_reader_error_redaction.py :44
import psycopg2 # noqa: E402
show 2 more
- db (weak) willows_grove-0.9.0/tests/test_grove_reader_unreachable.py :30
import psycopg2 # noqa: E402 - db (weak) willows_grove-0.9.0/tests/test_kart_reader.py :46
import psycopg2
tool registrations 23
- grove_list_channels willows_grove-0.9.0/grove/mcp_local.py :426
- grove_get_history willows_grove-0.9.0/grove/mcp_local.py :441
- grove_send_message willows_grove-0.9.0/grove/mcp_local.py :477
- grove_search willows_grove-0.9.0/grove/mcp_local.py :500
- grove_get_identity willows_grove-0.9.0/grove/mcp_local.py :529
- grove_watch willows_grove-0.9.0/grove/mcp_local.py :608
- grove_watch_all willows_grove-0.9.0/grove/mcp_local.py :632
- grove_reply willows_grove-0.9.0/grove/mcp_local.py :662
- grove_get_thread willows_grove-0.9.0/grove/mcp_local.py :688
- grove_flag willows_grove-0.9.0/grove/mcp_local.py :716
- grove_unflag willows_grove-0.9.0/grove/mcp_local.py :737
- grove_bus_send willows_grove-0.9.0/grove/mcp_local.py :756
show 11 more
- grove_bus_receive willows_grove-0.9.0/grove/mcp_local.py :799
- grove_bus_delete willows_grove-0.9.0/grove/mcp_local.py :825
- grove_ack willows_grove-0.9.0/grove/mcp_local.py :845
- grove_heartbeat willows_grove-0.9.0/grove/mcp_local.py :877
- grove_inbox willows_grove-0.9.0/grove/mcp_local.py :903
- grove_flagged willows_grove-0.9.0/grove/mcp_local.py :922
- grove_agents willows_grove-0.9.0/grove/mcp_local.py :970
- grove_fleet_status willows_grove-0.9.0/grove/mcp_local.py :981
- grove_mentions willows_grove-0.9.0/grove/mcp_local.py :997
- grove_human_required willows_grove-0.9.0/grove/mcp_local.py :1013
- grove_create_channel willows_grove-0.9.0/grove/mcp_local.py :1030
declared dependencies 9
- mcp@>=2.0.0,<3.0.0
- psycopg2-binary@>=2.9.0,<3
- cryptography@>=42.0.0,<50
- aiohttp@>=3.9.0,<4
- starlette@>=0.37.0,<2
- uvicorn@>=0.30.0,<1
- textual@>=0.61.0,<9
- pytest@>=8.0.0,<10
- pytest-timeout@*
transport_posture 1
- network-listening, binds all interfaces, no auth detected willows_grove-0.9.0/bridge/app.py :137
host="0.0.0.0", port=self._grove_port,