setup.sh targets an ubuntu server and is left untouched. this is the
laptop equivalent: file browser, claude/opencode chat, terminal. no go,
rust, cliamp, pulseaudio, neovim, shell dotfiles, vnc desktop, sudoers
grant or power management — 510 lines against 1033.
every step is optional, prompted, and presettable non-interactively with
SETUP_* variables, so it also works as a repair tool for one piece.
nothing calls sudo. node@22 goes on PATH with brew link --force, pm2 into
~/.local, and the claude cli no longer needs a /usr/local/bin symlink now
that the sidecar resolves it.
postgres is detected before anything is installed — a server already
listening on 5432 (docker) is used as-is.
notes on the differences from setup.sh:
- set -e is on, but every optional step is guarded, so a failure warns and
the run continues to a summary instead of aborting mid-way.
- .env is written 0600 and the generated JWT_SECRET is length-checked
before use, since jwt.ts throws on anything under 32 chars.
- PUBLIC_BUILD_ENV=development, which is what lets plain http://localhost
work with no reverse proxy in front.
- xcode command line tools are not required to build: node-pty and argon2
both ship darwin prebuilds. they still matter for git on a fresh mac.
ecosystem.mac.config.cjs drops officer-vnc (x11vnc needs xorg),
officer-email and officer-music, and pins cwd on every app so bun picks
up .env wherever pm2 is started from.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>