@metabase/rde 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +108 -61
  2. package/dist/cli.mjs +1944 -634
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @metabase/rde
2
2
 
3
- `rde` prepares a Mac or Linux machine for data-engineering work with Metabase and coding agents. One command, `rde init`, walks through everything a working setup needs: the `mb` CLI, the `rde` skill for your coding agents, a running Metabase Enterprise instance in Docker, an admin user, an API key wired into `mb`, your license, the databases you want to work with, and a git repository connected through Metabase's remote sync. Every step checks whether it is already done, so the wizard can be interrupted at any prompt and picked up again with the same command.
3
+ `rde` prepares a Mac or Linux machine for data-engineering work with Metabase and coding agents. One command, `rde init`, sets up everything a working setup needs: the `mb` CLI, the `rde` skill for your coding agents, a running Metabase Enterprise instance (in Docker, or a jar run with the machine's Java), an admin user, an API key wired into `mb`, a Postgres sample database with transforms switched on, your license, the databases you want to work with, and a git repository connected through Metabase's remote sync. It decides what it can on its own and asks only for what it cannot know: your license token, and with one, where the git repository lives. Every step checks whether it is already done, so the wizard can be interrupted at any prompt and picked up again with the same command.
4
4
 
5
5
  After `init`, the rest is lifecycle: `rde start`, `rde stop`, `rde status`, `rde logs`, `rde open`, and `rde db add` when another database comes along. The instance lives under `~/.rde`, listens on `127.0.0.1` only, and is used through `mb` and through the agent skill.
6
6
 
@@ -9,9 +9,11 @@ After `init`, the rest is lifecycle: `rde start`, `rde stop`, `rde status`, `rde
9
9
  - macOS or Linux
10
10
  - Node 20.19 or newer
11
11
  - git
12
- - Docker: Docker Desktop, OrbStack, or colima on a Mac; the `docker` package on Linux, with your user in the `docker` group
12
+ - One way to run Metabase, either of:
13
+ - Docker: Docker Desktop, OrbStack, or colima on a Mac; the `docker` package on Linux, with your user in the `docker` group
14
+ - Java 21 or newer (Temurin, or your package manager's OpenJDK), found through `JAVA_HOME` or on `PATH`
13
15
 
14
- `rde init` checks all four first and prints what is missing with the install hint for your platform.
16
+ `rde init` checks all of these first and prints what is missing with the install hint for your platform. Docker and Java each stand in for the other: with only one of them present the check passes and the missing one is listed as optional.
15
17
 
16
18
  ## Install
17
19
 
@@ -33,27 +35,31 @@ The wizard shows its checklist first (`✓` done, `○` to do, `–` not applica
33
35
  ○ Start Metabase
34
36
  ○ Create the admin user
35
37
  ○ Connect the Metabase CLI
38
+ ○ Connect a sample database
36
39
  ○ Activate your license
37
40
  ○ Connect your databases
38
41
  – Connect a git repository
39
42
  ○ Summary
40
43
  │
41
- ◆ Install @metabase/cli globally with npm?
44
+ ● Running npm install -g @metabase/cli
42
45
  ```
43
46
 
47
+ The steps up to the license run without a question: `mb` is installed with the package manager that installed `rde`, the skill goes to every coding agent found on the machine, Metabase runs in Docker when Docker is there (the jar with your Java otherwise), on the first free port from 3100 up, with the default admin credentials, and a Postgres sample database comes up in a second container, connected to Metabase as a place transforms can write. Each of those choices is printed as it is made and can be overridden with a flag. The prompts that remain are the license token (Enter skips it) and, with a license that includes remote sync, where the git repository lives and which remote it uses. The databases step asks nothing while the sample database is the only one: pass `--database` to connect your own during `init`, or run `rde db add` afterwards.
48
+
44
49
  The git step reads `–` until a license with remote sync is active; it is checked again when its turn comes, so a run that activates a license connects the repository in the same pass.
45
50
 
46
51
  | Step | What it does |
47
52
  | --- | --- |
48
- | Check this machine | Platform, Node version, git, and a reachable Docker daemon |
53
+ | Check this machine | Platform, Node version, git, and a reachable Docker daemon or a Java 21+ runtime |
49
54
  | Install the Metabase CLI | `@metabase/cli` (`mb`) with the package manager that installed `rde` |
50
- | Install the rde skill | The `rde` skill from `metabase/agent-skills` for the agents you pick (Claude Code, Codex, Cursor, Gemini CLI, OpenCode, Windsurf) |
51
- | Install Metabase | Pulls `metabase/metabase-enterprise:latest` (or `--image`) and creates the container on port 13000 (or the first free port above it, or `--port`) |
52
- | Start Metabase | Starts the container and waits for `/api/health`, showing Metabase's own progress |
53
- | Create the admin user | Default credentials or your own, see below |
55
+ | Install the rde skill | The `rde` skill from `metabase/agent-skills` for every coding agent found on this machine (Claude Code, Codex, Cursor, Gemini CLI, OpenCode, Windsurf); `--agents` picks a different set, `--skip-agents` none |
56
+ | Install Metabase | **Docker** when it is available: pulls `metabase/metabase-dev:transform-tests-ee` (or `--image`) and creates the container. **Jar** otherwise, or with `--runtime jar`: downloads the latest enterprise release (or the tag or `.jar` path given as `--jar`) into `~/.rde/jars/` and runs it with your Java. Either way on port 3100, or the first free port above it, or `--port` |
57
+ | Start Metabase | Starts the container or the jar and waits for `/api/health`, showing Metabase's own progress |
58
+ | Create the admin user | `admin@metabase.com` / `metabase123`, or your own with `--admin prompted`, see below |
54
59
  | Connect the Metabase CLI | Creates an API key named `rde` and logs `mb` in with it under the profile `default`, so plain `mb …` uses the instance; if `default` already points at another Metabase, offers the named profile `rde` instead |
60
+ | Connect a sample database | Runs Metabase's Postgres sample data (`metabase/qa-databases:postgres-sample-15`) in a container named `rde-sampledb-default` on the first free port from 15432, connects it as `Sample Database`, and switches transforms on. It replaces the H2 sample database Metabase would otherwise bundle, which cannot be a transform target. Needs Docker, so a jar instance on a machine without Docker keeps the H2 one instead |
55
61
  | Activate your license | Optional. Paste a token, or press Enter to skip; remote sync needs one with the `remote-sync` feature |
56
- | Connect your databases | Paste a connection string (or fill the fields one by one), review, test, add; repeat |
62
+ | Connect your databases | Only when `--database` names one or `rde db add` runs: paste a connection string (or fill the fields one by one), review, test, add; repeat |
57
63
  | Connect a git repository | Only with a license. Initializes or reuses a repository and points Metabase's remote sync at it |
58
64
  | Summary | URL, login, `mb` profile, databases, repository, and the command that opens each detected agent |
59
65
 
@@ -68,40 +74,49 @@ The second run on the same machine detects every step as done and prints the sum
68
74
  ✓ Start Metabase
69
75
  ✓ Create the admin user
70
76
  ✓ Connect the Metabase CLI
77
+ ✓ Connect a sample database
71
78
  – Activate your license
72
79
  ✓ Connect your databases
73
80
  – Connect a git repository
74
81
  ○ Summary
75
82
  │
76
- ◇ Your environment ────────────────────────────────────────────────────╮
77
- │ │
78
- │ Metabase: http://localhost:13000 │
79
- │ Login: admin@metabase.com / metabase123 │
80
- │ Metabase CLI: mb <command> │
81
- │ Databases: │
82
- │ Sample Database (sqlite, sync complete) │
83
- │ warehouse (postgres, sync complete) │
84
- │ Remote sync: not configured (no license) │
85
- │ rde skill: installed for Claude Code │
86
- │ Start working: │
87
- │ claude (Claude Code) │
88
- │ Lifecycle: rde status, rde stop, rde start │
89
- │ │
90
- ├───────────────────────────────────────────────────────────────────────╯
83
+ ◇ Your environment ─────────────────────────────────────────────────────────────────╮
84
+ │ │
85
+ │ Metabase: http://localhost:3100 │
86
+ │ Login: admin@metabase.com / metabase123 │
87
+ │ Metabase CLI: mb <command> │
88
+ │ Databases: │
89
+ │ Sample Database (postgres, sync complete) │
90
+ │ warehouse (postgres, sync complete) │
91
+ │ Sample database: psql "postgres://metabase:metasample123@localhost:15432/sample" │
92
+ │ Remote sync: not configured (no license) │
93
+ │ rde skill: installed for Claude Code │
94
+ │ Start working: │
95
+ │ claude (Claude Code) │
96
+ │ Lifecycle: rde status, rde stop, rde start │
97
+ │ │
98
+ ├─────────────────────────────────────────────────────────────────────────────────────╯
91
99
  │
92
100
  └ rde init finished
93
101
  ```
94
102
 
95
103
  Ctrl-C at any prompt stops the run; `rde init` again continues from the first step that is not done. `rde init --from license` re-runs from a step on, `rde init --only databases` runs one step.
96
104
 
97
- ## Credentials
105
+ ## The sample database
106
+
107
+ Every instance gets a second container, `rde-sampledb-default`, running `metabase/qa-databases:postgres-sample-15`: the Postgres edition of Metabase's own sample data, the same tables and rows the bundled H2 sample database has (`orders`, `products`, `people`, `reviews`, `accounts`, `invoices`, `feedback`, `analytic_events`), with the user `metabase`, the password `metasample123`, and the database `sample`. `init` publishes it on `127.0.0.1` at the first free port from 15432 up (5432 is usually your own Postgres), connects it to Metabase as `Sample Database`, and sets `transforms-enabled`, so transforms have somewhere to write on the first run: the H2 sample database refuses to be a transform target, this one creates the target schema on demand. The `metabase` user is the cluster superuser, so any schema name works. Metabase is started with `MB_LOAD_SAMPLE_CONTENT=false`, so the H2 sample database and the example content built on it are never created and the Postgres one is the only `Sample Database`.
108
+
109
+ ```sh
110
+ psql "postgres://metabase:metasample123@localhost:15432/sample" # the summary and `rde status` show the port
111
+ ```
112
+
113
+ `rde start` and `rde stop` bring the container up and down with Metabase, `rde status` shows its state and sync, `rde doctor` checks it, and `rde uninstall` removes it (with `--remove-image`, its image too). The data lives inside the container: `stop`/`start` keep it, and a container that is removed comes back with the pristine sample data on the next `rde init`. The jar runtime gets the sample database as well whenever Docker is available; on a machine without Docker the step is `–` and there is no sample database at all (the H2 one is switched off for every instance).
98
114
 
99
- The admin-user step offers two variants:
115
+ ## Credentials
100
116
 
101
- - **Use default credentials**: `admin@metabase.com` / `metabase123`. Both are written to `~/.rde` and shown in the summary and in `rde status`, so agents and scripts can log in without asking you. Pick this for a local instance nobody else can reach.
102
- - **Enter my own**: email and password are asked once, sent to Metabase, and never written anywhere by `rde`.
117
+ The admin user is created with the default credentials `admin@metabase.com` / `metabase123`. Both are written to `~/.rde` and shown in the summary and in `rde status`, so agents and scripts can log in without asking you; the instance listens on this machine only, so nobody else can reach it. To use your own instead, run `rde init --admin prompted` (or pass `--admin-email` / `--admin-password`, or an `admin-user` section in the answers file): the email and password are asked once, sent to Metabase, and never written anywhere by `rde`.
103
118
 
104
- In both variants `rde` creates an API key named `rde` in the Administrators group and stores it in the instance's state file with mode `0600`. That key is what `mb` and `rde` itself use afterwards.
119
+ In both variants `rde` creates an API key named `rde` in the Administrators group and stores it in the instance's state file with mode `0600`. That key is what `mb` and `rde` itself use afterwards. The sample database's own credentials (`metabase` / `metasample123`) are baked into its image and are not a secret.
105
120
 
106
121
  ## What lands in `~/.rde`, and what never does
107
122
 
@@ -109,30 +124,39 @@ In both variants `rde` creates an API key named `rde` in the Administrators grou
109
124
  ~/.rde/
110
125
  config.json { schemaVersion, defaultInstance }
111
126
  instances/default/
112
- state.json mode 0600: runtime, url, admin credentials variant, API key, license
113
- features, databases, git remote, step records
114
- data/ mounted into the container as /rde
127
+ state.json mode 0600: runtime, url, Metabase version, admin credentials variant,
128
+ API key, license features, databases, sample database, git remote, step records
129
+ data/ mounted into the container as /rde, or the jar's working directory
115
130
  metabase.db/metabase.db.mv.db the H2 application database
116
131
  plugins/ driver jars (Oracle, Vertica) go here
117
132
  content.git/ the bare repository behind a local git remote
133
+ backups/<timestamp>/ a copy of data/ taken by `rde upgrade`, one per upgrade
134
+ metabase.log jar runtime only: what Metabase prints
135
+ metabase.pid jar runtime only: the pid of the running Metabase
136
+ jars/<release tag>/metabase.jar downloaded releases, shared by every instance
118
137
  ```
119
138
 
120
- Three things are never written to disk or to a log by `rde`: the license token (it goes to Metabase, which keeps it in its own application database), a password you typed in the "Enter my own" variant, and a git access token for a hosted remote (Metabase encrypts it on its side). The `mb` profile `rde` lives where `mb` keeps its profiles, under `$XDG_CONFIG_HOME` or `~/.config`.
139
+ Three things are never written to disk or to a log by `rde`: the license token (it goes to Metabase, which keeps it in its own application database), a password you typed for a prompted admin user, and a git access token for a hosted remote (Metabase encrypts it on its side). The `mb` profile `rde` lives where `mb` keeps its profiles, under `$XDG_CONFIG_HOME` or `~/.config`.
140
+
141
+ The instance listens on `127.0.0.1` only (the container's published port, or the jar's `MB_JETTY_HOST`), so it is reachable from this machine and from nothing else on the network; the sample database's container is published the same way. Two containers belong to the instance: `rde-metabase-default` (Metabase, with `data/` mounted) and `rde-sampledb-default` (Postgres, no mount).
142
+
143
+ ### Docker or jar
121
144
 
122
- The Metabase container publishes its port on `127.0.0.1` only, so the instance is reachable from this machine and from nothing else on the network.
145
+ Docker is used whenever it is available: the image is self-contained, and the container is the thing to look at when something is off. The jar runtime is what `init` falls back to on a machine without Docker, and what `--runtime jar` (or `--jar`) picks on purpose, for running a jar you built yourself. It runs `java -jar` detached from the terminal, with the same data directory layout, the same environment, and the same `127.0.0.1`-only listener; `rde stop` sends it SIGTERM and waits for it to exit; `rde logs` reads `metabase.log`. Java is resolved through `JAVA_HOME`, then `PATH`, at every start, so a version manager's switch is picked up without reconfiguring. Without `--jar` the latest enterprise release is used; `--jar` accepts `latest`, a release tag such as `v1.63.18` (with or without the `v`; `v0.…` is the OSS build), or the path of a `.jar` file. A release is downloaded once into `~/.rde/jars/<tag>/` and reused; a path is run from where it is. A connection string that says `localhost` reaches your own databases directly, since the jar runs on the host; no rewrite to `host.docker.internal` is needed.
123
146
 
124
147
  ## Everyday commands
125
148
 
126
149
  | Command | What it does |
127
150
  | --- | --- |
128
- | `rde start` | Starts the instance and waits until it is healthy |
129
- | `rde stop` | Stops it; the data stays |
130
- | `rde status` | The init checklist, container state, health, databases with their sync state, remote sync state; `--json` for scripts |
131
- | `rde logs` | The container's log; `-f` keeps streaming |
151
+ | `rde start` | Starts the sample database and the instance, and waits until Metabase is healthy |
152
+ | `rde stop` | Stops both; the data stays |
153
+ | `rde status` | The init checklist, runtime state, health, databases with their sync state, the sample database's container and connection string, remote sync state; `--json` for scripts |
154
+ | `rde logs` | The container's log, or the jar's `metabase.log`; `-f` keeps streaming |
132
155
  | `rde open` | Opens the instance in the browser |
133
156
  | `rde db add` | The same database wizard as in `init` |
134
157
  | `rde db list` | The connected databases; `--json` for scripts |
135
158
  | `rde doctor` | One table of checks with a fix hint per failure; `--fix` re-runs the init steps behind them; `--json` for scripts; exit 1 while anything fails |
159
+ | `rde upgrade` | Moves the instance to a newer Metabase after backing up its data; see [Upgrading](#upgrading) |
136
160
  | `rde uninstall` | Removes the instance, its data, and the `mb` profile; see [Resetting](#resetting) |
137
161
 
138
162
  Everything else goes through the Metabase CLI, logged in as the profile `default`, so plain `mb …` talks to the instance:
@@ -162,25 +186,25 @@ git pull # after an export, to see the YAML files
162
186
 
163
187
  ## Running `init` without a terminal
164
188
 
165
- Every prompt can be answered ahead of time, by a flag or by a JSON answers file, so an agent or a CI job can run `rde init` unattended. When stdin is not a terminal, the first prompt nothing answers fails with exit code 2 and a message naming the step, the prompt, and the flag or answers key that would have answered it; nothing hangs. A text prompt with a default (image, port, display name) takes its default, as Enter would.
189
+ Every prompt can be answered ahead of time, by a flag or by a JSON answers file, so an agent or a CI job can run `rde init` unattended. The choices `init` makes on its own need no answer, so a run without a terminal only has to cover the license, the databases, and the git repository. When stdin is not a terminal, the first prompt nothing answers fails with exit code 2 and a message naming the step, the prompt, and the flag or answers key that would have answered it; nothing hangs. A text prompt with a default (a display name) takes its default, as Enter would.
166
190
 
167
191
  ### Flags
168
192
 
169
193
  ```sh
170
- rde init --agents claude-code,codex --admin default --skip-license \
194
+ rde init --skip-license \
171
195
  --database "postgres=postgres://user:pass@localhost:5432/app" \
172
196
  --git-dir ~/analytics --git-remote local
173
197
  ```
174
198
 
175
199
  | Flag | Answers |
176
200
  | --- | --- |
177
- | `--agents claude-code,codex` / `--skip-agents` | Which agents get the skill |
178
- | `--image`, `--port` | The Docker image and host port |
179
- | `--admin default\|prompted`, `--admin-email`, `--admin-password` | The admin user; an email or password implies `prompted` |
201
+ | `--agents claude-code,codex` / `--skip-agents` | Which agents get the skill, instead of the ones found on the machine |
202
+ | `--runtime docker\|jar`, `--image`, `--jar`, `--port` | How Metabase runs, the Docker image or the jar (`latest`, a release tag, or a `.jar` path), and the host port, instead of Docker when available, `metabase/metabase-dev:transform-tests-ee`, the latest release, and the first free port from 3100; `--image` implies `docker` and `--jar` implies `jar` |
203
+ | `--admin default\|prompted`, `--admin-email`, `--admin-password` | The admin user, `default` unless given; an email or password implies `prompted` |
180
204
  | `--license-token` / `--skip-license` | The license |
181
205
  | `--database <engine>=<connection string>` (repeatable) / `--skip-databases` | The warehouses |
182
206
  | `--git-dir`, `--git-remote local\|hosted`, `--git-url`, `--git-token` / `--skip-git` | The content repository; a remote or URL without `--git-dir` means the current directory |
183
- | `--yes` | Every confirmation (installing `mb`, skipping databases) |
207
+ | `--yes` | Every confirmation (skipping databases) |
184
208
 
185
209
  A secret flag (`--admin-password`, `--license-token`, `--git-token`) takes `-` to read one line from stdin, the way `mb auth login` takes its key, or a literal `$VAR` (single-quoted, so the shell leaves it alone) to read the environment; either keeps the value out of your shell history and process listings. Only one flag can read stdin.
186
210
 
@@ -191,11 +215,10 @@ rde init --license-token '$RDE_LICENSE_TOKEN' …
191
215
 
192
216
  ### Answers file
193
217
 
194
- `rde init --answers <file>` reads a JSON file with one section per step; flags win over the file, key by key. A `$VAR` string is read from the environment, and an unset variable is an error rather than an empty answer.
218
+ `rde init --answers <file>` reads a JSON file with one section per step; flags win over the file, key by key. A `$VAR` string is read from the environment, and an unset variable is an error rather than an empty answer. The `agents-skills`, `runtime`, and `admin-user` sections override what `init` would decide on its own and can be left out.
195
219
 
196
220
  ```json
197
221
  {
198
- "install-mb": { "package-manager": "npm" },
199
222
  "agents-skills": { "agents": ["claude-code"] },
200
223
  "runtime": { "image": "metabase/metabase-enterprise:latest", "port": 13000 },
201
224
  "admin-user": { "variant": "prompted", "email": "me@example.com", "password": "$RDE_ADMIN_PASSWORD" },
@@ -208,32 +231,50 @@ rde init --license-token '$RDE_LICENSE_TOKEN' …
208
231
  }
209
232
  ```
210
233
 
211
- `"license": { "token": null }` skips the license, `"databases": []` connects none, `"git-sync": { "location": "skip" }` leaves the repository for later. A database is pasted as a `uri` or entered with `fields`, keyed by the driver's field names as the admin form shows them; a file field takes a path. Anything a `fields` entry leaves out is asked in the terminal, or named in the error without one.
234
+ `"runtime"` takes `"kind": "docker"` or `"jar"`, which `"image"` and `"jar"` imply on their own (`"runtime": { "jar": "latest" }` runs the latest release with Java). `"license": { "token": null }` skips the license, `"databases": []` connects none, `"git-sync": { "location": "skip" }` leaves the repository for later. A database is pasted as a `uri` or entered with `fields`, keyed by the driver's field names as the admin form shows them; a file field takes a path. Anything a `fields` entry leaves out is asked in the terminal, or named in the error without one.
212
235
 
213
236
  ### Recording a run
214
237
 
215
- `rde init --print-answers <file>` writes, once an interactive run finishes, the answers file that reproduces it. Secrets are written as references, never as values: `$RDE_ADMIN_PASSWORD`, `$RDE_LICENSE_TOKEN`, `$RDE_GIT_TOKEN`, `$RDE_DATABASE_<n>_URI` for a connection string carrying a password, and `$RDE_DATABASE_<n>_<FIELD>` for a secret field. Set those variables and run `rde init --answers <file>` on the next machine.
238
+ `rde init --print-answers <file>` writes, once an interactive run finishes, the answers file that reproduces it: the prompts as they were answered, plus any override given by a flag. Secrets are written as references, never as values: `$RDE_ADMIN_PASSWORD`, `$RDE_LICENSE_TOKEN`, `$RDE_GIT_TOKEN`, `$RDE_DATABASE_<n>_URI` for a connection string carrying a password, and `$RDE_DATABASE_<n>_<FIELD>` for a secret field. Set those variables and run `rde init --answers <file>` on the next machine.
239
+
240
+ ## Upgrading
241
+
242
+ `rde upgrade` moves the instance to a newer Metabase without losing its application database. It makes sure the instance is running and reads the version it is on, fetches the target, and stops there with `Already on …` when the target is what the instance already runs. Otherwise it shows the running version, the target, and the backup path, asks once (`--yes` skips the question; without a terminal `--yes` is required), and then stops Metabase, copies the whole `data/` directory to `~/.rde/instances/default/backups/<timestamp>/`, switches the instance to the target, starts it, waits for health, and records the new version. Metabase migrates its application database forward on that first start.
243
+
244
+ For a Docker instance the target is an image ref (`--to <image ref>`; by default the instance's own image, which for a `:latest` tag means "whatever is latest now"), the pulled image is compared by id with the one the container runs, and the container is removed and created again from the target with the same port, mount, and environment. For a jar instance the target is `latest` (the default), a release tag, or a `.jar` path; a release is downloaded into `~/.rde/jars/` before the question, and the instance's `runtime.source` is switched to it.
245
+
246
+ ```sh
247
+ rde upgrade # Docker: pull the instance's image again; jar: the latest release
248
+ rde upgrade --to metabase/metabase-enterprise:v1.64.0 # Docker: a specific release
249
+ rde upgrade --to v1.64.0 # jar: a specific release
250
+ rde upgrade --to ~/metabase/target/uberjar/metabase.jar # jar: a build of your own
251
+ rde status # shows `Version: v1.64.0`
252
+ ```
253
+
254
+ Downgrades are refused: a target whose tag names an older release than the running one (`v1.62.0` against a running `v1.63.18`) ends with exit code 2 before anything is fetched, because those migrations only run forward. An older release needs a fresh instance (`rde uninstall`, then `rde init`). A target that carries no release tag (`latest`, a branch image, a jar path) is taken on trust.
255
+
256
+ If Metabase does not become healthy on the new version, nothing is rolled back automatically, since its migrations may already have run; the command prints the last log lines, the backup path, and the steps to go back: `rde stop`, replace `data/` with the backup, set `runtime.image` (or `runtime.source`) back to the previous value in `state.json`, for Docker `docker rm -f rde-metabase-default` and `rde init --only runtime`, then `rde start`. Backups are plain directories; delete the ones you no longer need. `rde uninstall` removes them with the instance directory.
216
257
 
217
258
  ## Resetting
218
259
 
219
- `rde uninstall` returns the machine to the state before `rde init`: it stops and removes the container, forgets the `mb` profile it logged in (`default`, or `rde`; never a profile pointing at another Metabase), and deletes `~/.rde/instances/default` along with the `config.json` entry. It shows the list first and asks once; `--yes` skips the question. Your working directory and its git history are never touched, nor are the `mb` CLI and Docker itself.
260
+ `rde uninstall` returns the machine to the state before `rde init`: it stops and removes the Metabase container, or stops the jar's process, stops and removes the sample database container, forgets the `mb` profile it logged in (`default`, or `rde`; never a profile pointing at another Metabase), and deletes `~/.rde/instances/default` along with the `config.json` entry. It shows the list first and asks once; `--yes` skips the question. Your working directory and its git history are never touched, nor are the `mb` CLI, Docker, and Java themselves.
220
261
 
221
262
  ```sh
222
- rde uninstall # container, mb profile, ~/.rde/instances/default
223
- rde uninstall --keep-data # container and profile only; `rde init` reinstalls from the kept data
263
+ rde uninstall # container or process, mb profile, ~/.rde/instances/default
264
+ rde uninstall --keep-data # container or process and profile only; `rde init` reinstalls from the kept data
224
265
  rde uninstall --remove-skill # also the rde skill from your coding agents
225
- rde uninstall --remove-image # also the Docker image (otherwise it stays for the next init)
266
+ rde uninstall --remove-image # also the Docker images or the downloaded jar (otherwise they stay for the next init)
226
267
  ```
227
268
 
228
- Running it twice is a no-op. It also works with no `~/.rde` at all, so a leftover `rde-metabase-default` container or `mb` profile from a half-finished run is cleaned up the same way. Anything it could not remove is listed at the end and the command exits with 1.
269
+ Running it twice is a no-op. It also works with no `~/.rde` at all, so a leftover `rde-metabase-default` or `rde-sampledb-default` container, a Metabase process recorded in a stray pid file, or an `mb` profile from a half-finished run is cleaned up the same way. Anything it could not remove is listed at the end and the command exits with 1.
229
270
 
230
271
  ## Troubleshooting
231
272
 
232
- Start with `rde doctor`. It checks the machine (platform, Node, git, Docker, `mb`), the instance (container, port, health, admin user, API key, `mb` profile, license, databases, remote sync), and the skill for each detected coding agent, one row each with ✓, ✗, or – and a hint under every ✗:
273
+ Start with `rde doctor`. It checks the machine (platform, Node, git, Docker, Java, `mb`), the instance (the container or the jar process, port, health, Metabase version, admin user, API key, `mb` profile, license, databases, the sample database's container, port, and Metabase record, remote sync), and the skill for each detected coding agent, one row each with ✓, ✗, or – and a hint under every ✗. Of Docker and Java, the one the instance does not use is `–` when missing; the one it uses is ✗.
233
274
 
234
275
  ```
235
- ✓ container rde-metabase-default stopped (metabase/metabase-enterprise:latest)
236
- ✓ port 13000 is free
276
+ ✓ container rde-metabase-default stopped (metabase/metabase-dev:transform-tests-ee)
277
+ ✓ port 3100 is free
237
278
  ✗ health unreachable
238
279
  → `rde start`
239
280
  – admin user Metabase is not healthy
@@ -241,7 +282,7 @@ Start with `rde doctor`. It checks the machine (platform, Node, git, Docker, `mb
241
282
 
242
283
  `rde doctor --fix` lists the init steps that repair the failed rows, asks once (`--yes` skips the question), re-runs them, and prints the table again. A failure with no automatic fix, such as a port held by another program, keeps its hint and the exit code stays 1 until it is resolved.
243
284
 
244
- **Docker is not running.** The first step prints the check that failed, with what Docker said and what to do, and exits with code 2:
285
+ **Neither Docker nor Java is available.** The first step prints the checks that failed, with what Docker said and what to do, and exits with code 2; with Java 21+ installed the Docker line is `–` instead and the wizard runs Metabase as a jar:
245
286
 
246
287
  ```
247
288
  ◇ This machine ──────────────────────────────────────────────────────────────────────────────────╮
@@ -253,14 +294,20 @@ Start with `rde doctor`. It checks the machine (platform, Node, git, Docker, `mb
253
294
  │ daemon running? │
254
295
  │ Install Docker Desktop (https://www.docker.com/products/docker-desktop/), OrbStack, or │
255
296
  │ colima, and start it │
297
+ │ ✗ Java: java is not installed │
298
+ │ Install Java 21 or newer: brew install --cask temurin@21, or from https://adoptium.net │
256
299
  │ │
257
300
  ├─────────────────────────────────────────────────────────────────────────────────────────────────╯
258
301
  Fix the items above and run rde init again
259
302
  ```
260
303
 
261
- Start Docker (or `colima start`) and run `rde init` again. `rde start` and `rde status` report the same condition on an existing instance.
304
+ Start Docker (or `colima start`), or install Java, and run `rde init` again. `rde start` and `rde status` report the same condition on an existing instance. An instance that was set up with Docker keeps needing Docker: `rde doctor` marks it ✗ then, whatever Java is there.
305
+
306
+ **The instance is not on port 3100.** The runtime step takes the first free port from 3100 up and says so: `Port 3100 is in use; using 3101`. 3100 rather than 3000 because a Metabase checkout's dev server, and most other local tools, sit on 3000 and its neighbours. `rde status` and the summary show the URL; `--port` pins a port of your choice and refuses a busy one. A container that exists with a different port or image is reported with the `docker rm -f` command that clears it.
307
+
308
+ **The sample database is not on port 15432.** The same rule: `Port 15432 is in use; using 15433 for the sample database`. The summary, `rde status`, and the `psql` line carry the port that was taken, and Metabase's connection uses it too. A sample database container that was removed by hand comes back on the next `rde init` (`rde doctor` points at it); if another program took its port meanwhile, it moves to the next free one and `init` says which Metabase database still points at the old port.
262
309
 
263
- **The port is in use.** Port 13000 is the default because 3000 is usually taken by a Metabase checkout's dev server. When 13000 is busy too, the runtime step says so and offers the next free port: `Port 13000 is in use by another program; 13001 is free`. Accept it, type another, or pass `--port`. A container that exists with a different port or image is reported with the `docker rm -f` command that clears it.
310
+ **The jar's Java is wrong.** The jar runtime needs Java 21 or newer and says which one it found: `Java 17.0.9 at /usr/bin/java is too old`. Point `JAVA_HOME` at a newer runtime, or put one first on `PATH`; the next `rde start` uses it. A pid file left by a reboot is recognized as stale (the pid is free, or runs something else) and ignored.
264
311
 
265
312
  **The license token is rejected.** Activation ends with `Metabase reports the token as invalid` (or another status Metabase returned). Check the token, and check that the container can reach `token-check.metabase.com`: activation needs outbound network access. Run `rde init --from license` to try again; press Enter at the prompt to skip the license and carry on without remote sync.
266
313
 
@@ -272,7 +319,7 @@ bun run check # tsc, oxlint + boundary linter, oxfmt, unit tests
272
319
  bin/rde-dev init # runs from source with RDE_HOME and the mb config under .dev-state/
273
320
  ```
274
321
 
275
- The end-to-end lane runs the built binary against real Docker: a seeded Postgres 16 sidecar on `127.0.0.1:15432` (`RDE_E2E_PG_PORT` to change it) and a fresh instance under `.e2e-state/`, with a fake `HOME` so the skill installer never touches your agents. `bun run build` must precede `bun run test:e2e`.
322
+ The end-to-end lane runs the built binary against real Docker: a seeded Postgres 16 sidecar on `127.0.0.1:15432` (`RDE_E2E_PG_PORT` to change it) and a fresh instance under `.e2e-state/`, with a fake `HOME` so the skill installer never touches your agents. `bun run build` must precede `bun run test:e2e`. With `RDE_E2E_JAR` pointing at a Metabase jar (a local build) the lane also runs the jar runtime through init, doctor, stop, start, and uninstall with the host's Java; without it those tests are reported as skipped.
276
323
 
277
324
  ```sh
278
325
  bun run build && bun run e2e:up && bun run test:e2e