Skip to content

rosepetal-meta-cli

Interactive installer that turns a vanilla Ubuntu machine into a Rosepetal Controller. A single binary, a single command, zero state files: each run inspects the host's real state and decides what to do.

Install

The CLI lives in a public GCS bucket. To install the latest prod binary on a fresh controller:

bash
curl -fsSL https://storage.googleapis.com/rosepetal-cli-dist/install.sh | sudo bash

This downloads rosepetal-cli to /usr/local/bin/ and makes it executable. Re-run the same command any time to update to the latest prod release.

Then start the install flow:

bash
sudo rosepetal-cli

Flow at a glance

                vanilla Ubuntu


              sudo rosepetal-cli            [Install controller]

       ┌───────────────┴──────────────┐
       ▼                              ▼
   PHASE 1: apt installs        PHASE 2: configure + redeem + install
   - baseline tools             - prompt token + redeem (gets compose YAML,
   - OpenSSH                      Tailscale authkey, AR pull token)
   - NVIDIA driver              - create user 'rosepetal'
   - Docker + nvidia-docker2    - delete every other UID≥1000 login user
   - Tailscale binary           - set hostname to device_name
                                - disable unattended-upgrades / apport
   (no token needed yet)        - install rosepetal-network systemd unit
                                - tailscaled syslog override + restart
   if NVIDIA module not loaded  - tailscale up --auth-key=...
       → ask reboot, exit       - write /opt/rosepetal/{compose.yml,.env,proxy.sh}
                                - docker login + docker compose up -d + logout
                                - pre-pull auxiliary node-red images
                                - run /opt/rosepetal/proxy.sh (funnels)
                                  → install complete

The PHASE selection is automatic. The CLI runs command -v docker, dpkg-query, /proc/modules etc. to find out what state the host is in, and dispatches accordingly. No state.json, no marker files, no saved tokens. Re-running after a successful install with a fresh token will swap the controller's identity (overwrites /opt/rosepetal/*, re-joins the tailnet, re-runs compose).

Requirements

  • Ubuntu (jammy/22.04 or noble/24.04 — anything Docker's official apt repos support, paired with NVIDIA's libnvidia-container repo).
  • Root via sudo.
  • Outbound internet to: apt repos (Ubuntu, Docker, NVIDIA), Tailscale's install script, the meta-api Cloud Run URL, and the Artifact Registry host that meta-api gives back in the redeem response (europe-southwest1-docker.pkg.dev).
  • An enrollment token (RPC1-XXXX-YYYY) minted via POST /devices/enroll on the meta-api. Required for Phase 2 only; Phase 1 doesn't need one.

Usage

sudo rosepetal-cli

The CLI shows a single-choice menu. Pick "Install controller" and follow the prompts. Phase 1 walks unattended through the apt installs; Phase 2 asks for the enrollment token after a host check.

If Phase 1 just finished and the NVIDIA kernel module isn't loaded yet, the CLI offers to reboot. After reboot, re-run the CLI — it detects the loaded module and skips straight to Phase 2.

Installing over SSH is safe — even through the controller's own Tailscale address. Once the token is redeemed, the disruptive tail of the install (wipe of previous state, Tailscale join, docker pulls, finalizing reboot) runs as a transient systemd unit (rosepetal-install) that keeps going if the session drops; the CLI just tails its log (/var/log/rosepetal-install.log). If you get disconnected — expected when the install resets the Tailscale identity you're connected through — reconnect (LAN IP, or ssh rosepetal@<device-name> once the new identity is up) and re-run sudo rosepetal-cli to re-attach to the running install.

Flags

There are none today. Future flags (overriding the API URL, dry-run, etc.) will live behind subcommands once we have more than one user action.

Defaults

API URLhttps://meta-api-7qq323bz2a-uc.a.run.app (override via ROSEPETAL_API_URL env var)
install dir/opt/rosepetal/
userrosepetal (sudo + docker groups) + password
Tailscale device tagstag:prod, tag:maintenance
Tailscale operatorrosepetal
NVIDIA drivernvidia-driver-580-open

Hardcoded credentials are inherited from the manual install script (rosepetal-dep-controller/postinstall-firstboot.sh) for parity. Known security gap — see v2 TODO.

Build

GOOS=linux GOARCH=amd64 go build -o rosepetal-cli .

The compose template / network script / proxy.sh are embedded via //go:embed into internal/install/assets.go, so the final binary has no runtime file dependencies beyond apt, docker, tailscale, and the system utilities Phase 1 installs.

For local builds during development:

go build .       # native architecture

Layout

rosepetal-meta-cli/
├── main.go                       # entry, menu, dispatch
├── go.mod / go.sum
├── README.md
└── internal/install/
    ├── orchestrator.go           # phase detection + dispatch
    ├── detect.go                 # state checks (command/dpkg/modules)
    ├── prompts.go                # menu + token + y/n
    ├── api.go                    # /devices/enroll/redeem HTTP client
    ├── exec.go                   # shell helpers (run/capture/atomic write)
    ├── assets.go                 # //go:embed declarations
    ├── phase1.go                 # apt installs (Docker, NVIDIA, Tailscale, ...)
    ├── phase2.go                 # orchestrator for the Rosepetal install
    ├── phase2_system.go          # user create + cleanup, hostname, hardening, network svc
    ├── phase2_tailscale.go       # tailscaled override + tailscale up + status parse
    ├── phase2_stack.go           # /opt/rosepetal/ + docker login + compose up + proxy
    ├── phase2_detach.go          # SSH-disconnect-proof background tail (systemd-run)
    ├── phase2_wipe.go            # detect + wipe previous controller state
    └── assets/
        ├── proxy.sh              # Tailscale funnels (verbatim from controller bundle)
        ├── rosepetal-network.sh  # NIC tuning + sysctl + cpufreq
        └── rosepetal-network.service

Known v1 limitations

  • NVIDIA driver install requires reboot — the CLI does NOT auto-reboot; it prompts the operator. Re-running after reboot is automatic.
  • No update / uninstall / status subcommands. Re-running install with a new token is the supported "update" path (overwrites /opt/rosepetal/* and re-applies compose).
  • proxy.sh failures are surfaced as warnings, not errors — the stack is considered "up" even if HTTPS funnels didn't fully apply.