Install, and What Needs Root
Installing takes two commands and about a minute. This chapter spends longer than that explaining what they do, because a tool that asks for your administrator password has an obligation to be legible — and because Grove's answer here is unusual.
The problem
Developer tools ask for sudo casually. An
installer wants it, a package manager wants it, a helper
daemon wants it forever. Each time the justification is
something small and true — a port, a system file
— and the real cost never appears on the invoice:
every bug in that process is now a root bug on your
machine.
You should know what you are agreeing to. So here it is, line by line.
The two commands
sudo grove init # config, root CA, a PHP build, resolver + trust
sudo grove install # the background service
grove init does four things.
It writes config.toml. It creates a local
certificate authority and adds it to your system trust
store — that is chapter 4, and it is the part that
needs your password. It downloads a PHP build. And it
writes /etc/resolver/test, a small file that
tells macOS to send every .test lookup to
Grove instead of to the internet.
grove install writes the
launchd unit that keeps the daemon running and restarts it
if it dies. On Linux it writes a systemd unit instead.
The daemon that serves your sites is not root
Here is the part worth reading twice, because it is the opposite of what the ports suggest.
Grove serves on 80, 443 and 53, and binding a port below 1024 requires privilege. The obvious conclusion — and the one Grove itself drew for its first several versions — is that the daemon must be root. It does not, because of one distinction: binding a privileged port needs root; serving on one does not.
A socket is a file descriptor. Whoever binds it needs privilege; whoever holds it afterwards needs none. And your machine already has a root process whose entire job is starting other processes. So launchd binds 53, 80 and 443 while it is root, then starts Grove as you and hands the already-listening descriptors over.
grove doctor
✓ privileges http_port=80, elevated=false, sockets from the
service manager: tcp/53, tcp/80, tcp/443, udp/53
elevated=false is your name on that line.
From its first instruction, the process handling every
request to your sites is running as you — and so is
everything it starts: PHP-FPM, PostgreSQL, MySQL, Redis,
the Vite server, Composer.
What still needs root is one-off and visible: writing the
unit file, writing /etc/resolver, and adding
the CA to the trust store. All three happen inside the two
commands above, while you are watching, having typed
sudo on purpose. Nothing that happens
while a request is being served is privileged.
Learn to run doctor
grove doctor is the command to reach for
before you start guessing, and it is worth running once
now while everything is fresh, so you know what healthy
looks like.
It checks the things that are true or false rather than matters of opinion: the daemon is running, the ports are listening, the resolver file exists and points at Grove, the CA is trusted by the system, the Grove home is owned by you and not world-writable, your certificates have not expired. Each line is a claim you could verify by hand; the value is that it verifies all of them in a second.
sudo grove install once
is the deliberate step that switches a machine over. It
also hands the Grove home to you in the same step, which
is why it is one command and not a surprise permission
error on Monday.
What you learned
- Two privileged commands, and both are legible. init writes config, CA, resolver; install writes the service unit.
- Binding a privileged port needs root; serving on one does not. launchd binds and hands the descriptor over.
- The daemon runs as you — and so does everything it starts.
elevated=falsein doctor is the proof. - What still needs root is one-off and visible, and happens while you are watching.
- Run doctor once while healthy, so you know what healthy looks like.