Skip to content

Repository files navigation

Elyra Grove

Elyra Grove

A native local development environment in Rust.

Grove serves *.test domains with automatic routing, local HTTPS, multi-version PHP and zero external dependencies — from a single Rust core.

CI Release License: MIT Rust Platform GUI

Grove dashboard — sites with local HTTPS and public tunnels


Why Grove

Local PHP/Laravel development today means choosing between friction points:

  • Laravel Valet is elegant and light, but macOS-only and leans on Homebrew + Composer + dnsmasq.
  • Herd ships static binaries but is closed, won't let you load custom PHP extensions, and gates databases, mail testing and dumps behind a Pro license.
  • Docker / Sail is flexible but heavy and slow for simple local work.

Grove takes a different path: one Rust codebase, three platforms, and nothing to install around it.

Valet Herd Docker/Sail Grove
Cross-platform macOS only macOS + Win ✅ ✅
No Homebrew/Composer/dnsmasq ❌ ✅ ➖ ✅
Custom / bring-your-own PHP ✅ ❌ ✅ ✅
Full extension set (intl, mysqli, pdo_sqlite…) ➖ ➖ ✅ ✅
Bundled static PHP ❌ ✅ ➖ ✅
Idle footprint tiny small heavy tiny
Open / no license wall ✅ ❌ ✅ ✅

Features

  • 🌐 Automatic *.test routing via an embedded DNS resolver — no manual hosts editing.
  • 🔒 Local HTTPS with a private root CA and on-demand per-site leaf certificates.
  • 🐘 Bundled PHP, with the extensions you actually need — install multiple self-contained versions (grove php install 8.5|8.4|8.3), with per-site isolate and lazy FPM pools. The prebuilt static-PHP sets each have a hole — one lacks pdo_sqlite/pdo_pgsql, the other lacks intl/mysqli — so Grove builds the union itself: both PDO drivers and intl, mysqli, sodium, readline, apcu, xsl. grove php ext audits any build and tells you what each gap would cost you.
  • 🐞 Step-debugging — grove debug on loads Xdebug into the FPM pools (trigger mode, zero idle overhead) so a DAP-capable editor can set breakpoints; grove debug env wires up CLI debugging (artisan, tests).
  • ⬢ Bundled Node.js — download node · npm · npx, with per-site Node versions; no nvm or Homebrew.
  • 🗄️ Bundled services — Grove downloads and supervises PostgreSQL, MySQL, ElyraSQL and Redis itself, so there is no database/cache or Homebrew to install separately. ElyraSQL is a single-binary, single-file MySQL-compatible server: your Laravel .env says DB_CONNECTION=mysql and nothing else changes.
  • 📧 Built-in mail-catcher — an SMTP server that captures outgoing mail, with a Mailpit-style viewer.
  • 🌍 Public tunnels — grove share exposes a local site to the internet (demos, webhooks) via a self-hostable, native Expose/ngrok alternative.
  • 📦 Reproducible environments — commit a grove.toml (PHP/Node versions, services, HTTPS, dev), and a teammate goes from git clone to a running, identical setup with one grove up. Or package the whole thing — config, .env, and database — into one shareable file with grove bundle export (no Docker).
  • ⇄ Request timeline, replay & test-gen — Grove is the proxy, so it records a live, framework-agnostic log of every request. Expand any request to see headers + body — credentials redacted, since the same data goes to your AI assistant over MCP — replay it (grove replay <id>, which uses the unredacted capture so it still works), or copy it as a curl/.http/Pest test — turn a failing request into a regression test in one click.
  • 🔗 Causal chain — turn on grove sql-capture and each request shows the SQL it issued and the mail it sent within its time window, plus derived metrics — "this 500 also ran 20 queries and sent an email" — with zero app instrumentation. Surfaced over MCP (grove_request_chain).
  • ✨ Explain this error — grove explain <id> (and the grove_explain MCP tool, plus an Explain button in the app) curate one bundle — the request, its causal chain, and the matching stacktrace from the Laravel log — ready to hand to your AI assistant.
  • 🪝 Local webhook hub — capture incoming webhooks at /__grove/hooks/<bucket> (a local webhook.site), expose them with grove share, inspect each payload, and re-deliver them to your app while you fix the handler. Herd catches mail; Grove catches webhooks too.
  • 🌿 Another branch beside yours — grove try <branch> checks the branch out in a worktree, serves it at <site>--<branch>.test with its own copy of your database, and migrates that copy. Your checkout and your database stay as they were. grove try --done takes it all away.
  • 🔍 Which commit broke this request? — grove bisect --good <ref> --request <id> replays a recorded request against each commit, each checked out beside yours with a fresh, migrated database, and lets git bisect name the first bad one.
  • 🔁 Replay against the same data — grove replay <id> --same-data snapshots the site's database on the first run and restores it before every later one, so a request that writes can be retried from the same starting point.
  • 🐢 Which route got slower — grove routes keeps each route's typical time and flags the ones that got at least twice as slow, with a request id to look at.
  • 🤖 AI tools (MCP) — grove mcp exposes your sites, requests, webhooks, logs, route timings and database schema to Claude/Cursor over the Model Context Protocol. Local-only and read-only by default. Ask your assistant "what did the last 500 look like?" or "show me the orders schema." With --allow-write, an agent can also open a sandbox: its own branch, site and database copy to work in, with nothing touching yours.
  • 🛠 Tools — migrate MySQL from Herd, and convert whole databases between MySQL, ElyraSQL, PostgreSQL and SQLite.
  • ⏱️ Database snapshots — grove db snapshot takes a point-in-time snapshot of the bundled MySQL/PostgreSQL (a SQL dump) or ElyraSQL (a hot copy of its one database file) and grove db restore rolls it back, so risky migrations are fearless.
  • 🌳 A database per git branch — grove db branches on gives each branch its own database and swaps it in when you check the branch out.
  • 💤 Databases that run only when used — grove service on-demand mysql on holds the port and starts the server on the first connection. It starts as soon as a site that uses it is looked up, and stops again after it has sat idle.
  • 🔀 Toolchain on your PATH — grove path install puts the bundled php, composer, cpx, node, npm and laravel on your PATH, auto-switching to each project's pinned version — so you can drop Herd/Valet entirely.
  • 📦 cpx built in — npx for Composer packages: cpx laravel/pint, cpx phpstan analyse, cpx friendsofphp/php-cs-fixer fix run a package's CLI without installing it in your project or globally. cpx exec -r '…' and cpx tinker run ad-hoc PHP with your app booted. Shipped as a shim over Grove's own PHP — nothing to composer global require.
  • 🐳 Docker / OrbStack aware — auto-discovers containers and serves them as <name>.test with trusted local HTTPS, next to native sites.
  • ⚡ Runs your dev processes — grove dev reads the processes your app declares via Laravel's DevCommands (artisan dev:list) and supervises them per site — Vite HMR, queue worker, and your own additions like Reverb or stripe listen. It skips serve (Grove is the server) and pail (the Logs panel), needs no open terminal, and falls back to Vite + a queue worker on older Laravel or non-Laravel sites. Use it instead of php artisan dev, not alongside it.
  • 🧩 Driver system — Laravel, WordPress, generic PHP, static sites, and reverse proxy (Vite/Node).
  • 🌊 Streaming both ways — responses are forwarded as PHP flushes them, so Server-Sent Events and long-lived streams work, and a large download costs no memory. Uploads stream too: a 400 MB upload moves through Grove in a few MB of RSS rather than a gigabyte.
  • 🚀 Nothing done twice — proxied requests reuse pooled connections instead of handshaking per asset, static files revalidate with an ETag (a reload of unchanged assets costs a 304, not a re-transfer), and DNS answers are cacheable so the system resolver stops asking on every connection.
  • 🛡 Built to stay up — a panic in one request stays in that request, the accept loop backs off instead of spinning a core when file descriptors run out, and silent connections are timed out rather than held forever.
  • 🌱 Create / import sites — scaffold a new Laravel or static project, or link existing ones.
  • 🖥️ GUI + CLI in parity — both are thin clients over the same daemon, plus a macOS menu-bar icon.
  • 🔌 Zero external dependencies — DNS, proxy, FastCGI and TLS are all built in.

Zero external dependencies

Grove has no runtime dependency on Homebrew, Composer, dnsmasq, OpenSSL or Laravel Valet. DNS, the reverse proxy, FastCGI and TLS are all built into the Rust core. Even PHP can be downloaded as a self-contained static binary via grove php install — it links only against the operating system's own libraries. grove import reads an existing Valet config if one is present, but it never requires Valet to be installed.

Quick start

📘 New here? Read the full installation guide — a step-by-step manual with example terminal output, a first site, HTTPS, services and troubleshooting.

# 1. First-run setup: config, root CA, a static PHP build, resolver + trust
sudo grove init

# 2. Install the background service (launchd/systemd binds 80/443/53 and hands
#    them to a daemon that runs as you; starts at boot)
sudo grove install

# 3. Point Grove at your projects
grove park ~/Code           # every subdirectory becomes <name>.test
#   or, inside one project:
grove link

# 4. Open https://myproject.test 🎉
grove secure myproject      # enable HTTPS
grove isolate myproject 8.3 # pin a PHP version for this site

The daemon binds privileged ports (53/80/443) and runs PHP as your user, so it is installed as a root service. sudo grove start works too for a foreground/one-off run.

From a clean machine to a running *.test Laravel app in under five minutes — no Homebrew, no Composer, no Valet.

Command reference

Category Commands
Setup init, ca trust / ca uninstall, install / uninstall (service)
Lifecycle daemon, start, stop, restart
Sites new, up (from grove.toml), park / unpark, link / unlink, list, secure / unsecure, isolate / unisolate, proxy
PHP php install, php register, php discover, php list, use
Node node list, node install <version>, node use <site> <version>, node unuse <site>
Services service list, service install, service start, service stop, service restart
Databases db snapshot, db list, db restore <id>, db rm <id>
Observability requests [site] (live request timeline), sql-capture on|off|status (SQL causal chain)
Toolchain path install, path show, path uninstall
Dev dev start [site], dev stop [site], dev list
Tunnels share <site> (public URL via grove-tunnel server)
Mail mail, mail show <id>, mail clear
Logs logs (list sources), logs <site> (view entries)
Operations status, doctor, env [site], import (Valet)

Every command supports --json for scripting and Elyra Conductor integration.

GUI (Tauri + Svelte)

Grove about

The GUI is a thin client that proxies everything to the daemon over the same grove-ipc JSON-RPC the CLI uses — they are always in parity. The frontend is Svelte 5 + Vite and shares the Elyra Conductor look & feel (Tokyo Night palette, JetBrains Mono). The dashboard surfaces every site with its driver, PHP version, a one-click HTTPS toggle, isolate, and shortcuts to open in the browser or Finder, alongside service, mail, tunnels, tools, logs and doctor panels. The Requests panel is a live, framework-agnostic timeline of every request Grove proxies (status colour-coding, slow-request highlighting, per-site filter). The Logs panel parses per-site Laravel logs and Grove's own service logs into a level/date/message view with a stacktrace detail pane. A Settings panel (⌘,) manages parked paths, the TLD, default PHP, the mail-catcher port, launch-at-login and the theme (auto/light/dark).

# Build the frontend (the GUI binary embeds it at compile time)
cd crates/grove-gui/ui && pnpm install && pnpm build && cd -

# Build and launch
cargo build --release -p grove-cli -p grove-gui
grove gui              # starts the daemon if needed, then opens the app

After changing the UI, rebuild the frontend and grove-gui so the new bundle is re-embedded. Closing the window keeps Grove running in the menu bar; quit via the menu-bar icon.

Configuration

Grove's source of truth is a single declarative TOML file ($GROVE_HOME/config.toml). Runtime state that can be re-derived is kept out of it, so the file stays human-readable and diff-friendly.

[general]
tld = "test"
default_php = "8.5"
auto_start = true

[[parked]]
path = "~/Code"

[services]
mail_enabled = true
mail_port = 1025

[[sites]]
name = "inside-next"
path = "~/Code/inside-next"
php = "8.5"          # per-site PHP
node = "22"          # per-site Node
secure = true
driver = "laravel"

[[sites]]
name = "frontend"
path = "~/Code/frontend"
driver = "proxy"
proxy_to = "http://127.0.0.1:5173"

Architecture

A single long-running daemon binds the privileged ports (DNS 53, HTTP 80, HTTPS 443) and supervises the FPM pools. The CLI and GUI are thin clients that talk to the daemon over local IPC.

grove-core      site registry, driver detection, config, paths   (pure, no OS I/O)
grove-ipc       JSON-RPC protocol + transport (CLI/GUI ↔ daemon)
grove-tls       root CA + leaf issuance (rcgen/rustls)
grove-dns       embedded resolver for *.<tld> (hickory)
grove-proxy     HTTP/HTTPS proxy + minimal FastCGI client (hyper)
grove-runtime   PHP version + FPM pool supervisor
grove-os        platform integration (resolver, trust store, elevation)
grove-daemon    long-running process: binds ports, serves IPC
grove-cli       clap frontend (binary: `grove`)
grove-gui       Tauri 2 + Svelte 5 desktop GUI (thin client over grove-ipc)

Building from source

# Requirements: Rust 1.94+, and (for the GUI) Node 20+ with pnpm.
cargo build --release        # build the CLI + daemon
cargo test                   # run the test suite

For local testing without binding privileged ports, set an isolated home and high ports:

export GROVE_HOME=/tmp/grove-home
mkdir -p "$GROVE_HOME"
cat > "$GROVE_HOME/config.toml" <<'EOF'
[general]
tld = "test"
default_php = "8.5"
http_port = 8080
https_port = 8443
dns_port = 5354

[[parked]]
path = "~/Code"
EOF
grove daemon

Installing the macOS app

From 0.1.2 the macOS app is code-signed with a Developer ID and notarized by Apple, so it opens normally — download the .dmg, drag Grove to /Applications, and launch it. No Gatekeeper warning, no workarounds.

To actually serve *.test (which needs ports 53/80/443), install the background service once: sudo grove install. The GUI is a dashboard over that daemon.

Apple Silicon only. Prebuilt macOS releases target aarch64 — notarizing the Intel build on GitHub's macos-13 runner routinely hangs, so it isn't shipped. On an Intel Mac, build from source: cargo build --release.

Grove Pro

Everything above is free and open source, forever — the core is never gated. Grove Pro ($99/seat/year) unlocks power features on top:

  • Database client — a built-in Database panel that auto-connects from each site's .env. Browsing + SELECT is free; Pro adds inline row editing, a schema inspector, and a production-safety guard.
  • End-to-end encrypted team secret sync (grove secret) — a project's .env shared securely, never pasted into a chat window. Encrypted on your machine (age/X25519); the backend only ever stores ciphertext.

Licenses are verified offline. See Pro & Teams.

Documentation

Contributing

Contributions are welcome! See CONTRIBUTING.md to get started, and please follow our Code of Conduct. For security issues, see SECURITY.md.

License

MIT

Built by Knut W. Horne · part of the Elyra ecosystem

About

A native local development environment in Rust — *.test domains, local HTTPS, multi-version PHP/Node and bundled databases, with zero external dependencies.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages