npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@metabase/rde

v0.1.11

Published

Prepares a local machine for data-engineering work with Metabase and coding agents

Readme

@metabase/rde

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, a connection string for your own database, and with a license, 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.

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.

Requirements

  • macOS or Linux
  • Node 20.19 or newer
  • git
  • One way to run Metabase, either of:
    • Docker: Docker Desktop, OrbStack, or colima on a Mac; the docker package on Linux, with your user in the docker group
    • Java 21 or newer (Temurin, or your package manager's OpenJDK), found through JAVA_HOME or on PATH

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.

Install

npm install -g @metabase/rde
rde init

What rde init does

The wizard shows its checklist first (✓ done, ○ to do, – not applicable), runs the steps still to do in order, and ends with a summary of your environment. A first run on a machine that has Docker but not yet mb starts like this:

┌  rde init
✓ Check this machine
○ Install the Metabase CLI
○ Install the rde and data-app skills for your coding agents
○ Install Metabase
○ Start Metabase
○ Create the admin user
○ Connect the Metabase CLI
○ Connect a sample database
○ Activate your license
○ Connect your databases
– Connect a git repository
○ Summary
│
●  Running npm install -g @metabase/cli@alpha-release-65-integration-branch

The steps up to the license run without a question, save one: mb is installed with the package manager that installed rde, the skill goes to the coding agents you pick from the ones found on the machine (at a terminal; without one, to every agent found), Metabase runs in Docker when Docker is there (the jar with your Java otherwise), on the first free port from 3100 up, with an admin user whose password is generated and kept in ~/.rde, mb is logged in under its default profile (the one question here comes when default already points at another Metabase), 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), a connection string for your own database (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 once. At a terminal, without --yes or --skip-databases, and while none of your own databases is connected, it lists what is connected (the sample database) and asks for a connection string: paste one and rde reads the engine from it (or asks which, when the string fits several, or shows the engine list when it fits none), then shows the connection for review, tests it, adds it, and offers another. Enter skips it, and the skip is remembered, so the next rde init does not ask again; rde init --only databases and rde db add still do. With --yes or without a terminal it asks nothing and the sample database stands in; --database connects yours ahead of time.

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.

| Step | What it does | | --- | --- | | Check this machine | Platform, Node version, git, and a reachable Docker daemon or a Java 21+ runtime | | Install the Metabase CLI | @metabase/cli (mb) at the alpha-release-65-integration-branch dist-tag, with the package manager that installed rde | | Install the rde and data-app skills | The data-app skills (metabase-data-app-setup, -routing, -actions, -semantic-layer) from metabase/metabase/skills#master, then the rde and rde-data-apps skills from the main branch of metabase/agent-skills (the rde skill hands an explicit app request to rde-data-apps, which hands the build to the data-app skills), for the coding agents you pick at a terminal, from the ones found on this machine (Claude Code, Codex, Cursor, Gemini CLI, OpenCode, Windsurf). When none is found, the step is skipped and says how to add the skills once an agent is installed; rde installs no agent. Without a terminal, or with --yes, every agent found gets the skills; --agents picks a set ahead of time (the skills are installed for them whether or not they are on the machine yet), --skip-agents none; RDE_SKILL_SOURCE installs the two rde skills from another URL or a local checkout of metabase/agent-skills | | Install Metabase | Docker when it is available: pulls metabase/metabase-enterprise-head:latest (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 | | Start Metabase | Starts the container or the jar and waits for /api/health, showing Metabase's own progress | | Create the admin user | [email protected] with a generated password that rde credentials shows, or your own with --admin prompted, see Credentials | | 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; when default already points at another Metabase it asks first, see The mb profile; a key Metabase rejects is replaced | | 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 (or one Docker picks, when another program or a concurrent rde init takes that port first), 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 | | Activate your license | Optional. Paste a token, or press Enter to skip; remote sync needs one with the remote-sync feature | | Connect your databases | At a terminal, until you connect one of your own or skip it: paste a connection string (Enter skips), review, test, add; repeat. --database names them ahead of time; with --yes or without a terminal nothing is asked. rde db add starts from the engine list, where the fields can also be filled one by one | | Connect a git repository | Only with a license. Initializes or reuses a repository and points Metabase's remote sync at it | | Summary | URL, login email, the mb command for the instance, databases, repository, and the command that opens each detected agent |

The second run on the same machine detects every step as done and prints the summary only:

┌  rde init
✓ Check this machine
✓ Install the Metabase CLI
✓ Install the rde and data-app skills for your coding agents
✓ Install Metabase
✓ Start Metabase
✓ Create the admin user
✓ Connect the Metabase CLI
✓ Connect a sample database
– Activate your license
✓ Connect your databases
– Connect a git repository
○ Summary
│
◇  Your environment ─────────────────────────────────────────────────────────────────╮
│                                                                                    │
│  Metabase: http://localhost:3100                                                   │
│  Login: [email protected] (rde credentials shows the password)                  │
│  Metabase CLI: mb <command>                                                        │
│  Databases:                                                                        │
│    Sample Database (postgres, sync complete)                                       │
│    warehouse (postgres, syncing; `rde status` shows when it finishes)              │
│  Sample database: psql "postgres://metabase:metasample123@localhost:15432/sample"  │
│  Remote sync: not configured (no license)                                          │
│  rde skill: installed for Claude Code                                              │
│  Start working:                                                                    │
│    claude  (Claude Code)                                                           │
│  Lifecycle: rde status (--json for scripts), rde stop, rde start                   │
│                                                                                    │
├────────────────────────────────────────────────────────────────────────────────────╯
│
└  rde init finished

Ctrl-C at any prompt stops the run; rde init again continues from the first step that is not done. A step that fails ends the run with exit code 1 (2 for a missing answer) and a last line naming the step and the command that resumes there: rde init stopped at "Connect a sample database": <reason>. Resume with rde init --from sample-database. rde init --from license re-runs from a step on (a later step that does not apply, such as the git step without a license that has remote sync, stays skipped), rde init --only databases runs one step.

The sample database

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.

psql "postgres://metabase:metasample123@localhost:15432/sample"   # the summary and `rde status` show the port

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).

Credentials

The admin user is [email protected] with a password rde generates: 32 random URL-safe characters, kept in ~/.rde/instances/default/secrets.json (mode 0600, in a 0700 directory) and nowhere else. The summary and rde status show only the email:

Login: [email protected] (rde credentials shows the password)

rde credentials prints the Metabase URL, the email, and the password, wherever its output goes, so a coding agent you ask for the login can run it and tell you; rde credentials --json prints the same as an object (url, email, password, and a note). rde credentials --api-key prints the API key and nothing else, for a script to hand to another tool without showing it, such as the DATA_APP_MB_API_KEY a data app reads from its repository's .env.local. Nothing else prints the password: not the summary, rde status (text or --json), rde doctor, a log, or an error message. To use your own login 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, and rde credentials says so.

In both variants rde creates an API key named rde in the Administrators group and keeps it in the same secrets file. That key is what mb and rde itself use afterwards; state.json and rde status --json never carry it. If Metabase stops accepting it (someone regenerated or revoked it in the admin settings), rde init --only api-key or rde doctor --fix makes a new one with the stored admin login and logs mb in again, asking nothing; with a prompted admin it asks for the password once.

An instance whose state file held the public [email protected] pair keeps working: the password and the key move to secrets.json the first time any rde command reads the state, and rde doctor marks the admin password legacy-default, because anyone who knows the default can log in. When the instance holds nothing you need, rde uninstall then rde init recreates it with a generated password.

The sample database's own credentials (metabase / metasample123) are baked into its image and are not a secret.

What lands in ~/.rde, and what never does

~/.rde/
  config.json                        { schemaVersion, defaultInstance }
  instances/default/                 mode 0700
    state.json                       mode 0600, no secrets: runtime, url, Metabase version, admin email and variant,
                                     mb profile, license features, databases, sample database, git remote, step records
    secrets.json                     mode 0600: { schemaVersion, admin: { email, password } | null, apiKey }
    data/                            mounted into the container as /rde, or the jar's working directory
      metabase.db/metabase.db.mv.db  the H2 application database
      plugins/                       driver jars (Oracle, Vertica) go here
      content.git/                   the bare repository behind a local git remote
    backups/<timestamp>/             a copy of data/ taken by `rde upgrade`, one per upgrade
    metabase.log                     jar runtime only: what Metabase prints
    metabase.pid                     jar runtime only: the pid of the running Metabase
  jars/<release tag>/metabase.jar    downloaded releases, shared by every instance

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 lives where mb keeps its profiles, under $XDG_CONFIG_HOME or ~/.config.

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). Those are the names under ~/.rde; with RDE_HOME set to another directory both names end in a tag derived from that directory, such as rde-metabase-default-3fa9c1d2, so two homes never share a container.

Docker or jar

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.

Everyday commands

| Command | What it does | | --- | --- | | rde start | Starts the sample database and the instance, and waits until Metabase is healthy | | rde stop | Stops both; the data stays | | rde status | The login email, the mb profile, the init checklist, whether setup is complete (or the first step init did not finish, with the rde init --from command that resumes there), runtime state, health, databases with their sync state, the sample database's container and connection string, remote sync state and the content repository's working directory (contentRepository); --json for scripts and agents (mbProfile, adminEmail, setup, never a secret); exit 0 whatever it reports | | rde credentials | The Metabase URL, admin email, and password; --json for scripts and agents; --api-key for the API key alone | | rde logs | The container's log, or the jar's metabase.log; -f keeps streaming | | rde open | Opens the instance in the browser | | rde db add | The database wizard: pick the engine, paste a connection string or fill the fields, review, test, add. rde db add --url <connection string> [--name <display name>] asks nothing: it reads the engine from the string (--engine for one that fits several), tests it and adds it, which is how a script or a coding agent connects a database without a terminal; --url - reads the string from stdin | | rde db list | The connected databases; --json for scripts; a bare rde db does the same | | rde doctor | One table of checks with a fix hint per failure; --fix re-runs the init steps behind them; --json for scripts and the rde skill (every row's id, status, detail, hint, and fix: the rde command that repairs it, or null when you have to act); exit 1 while anything fails | | rde upgrade | Moves the instance to a newer Metabase after backing up its data; see Upgrading | | rde uninstall | Removes the instance, its data, and the mb profile; see Resetting |

Everything else goes through the Metabase CLI, logged in as the profile default, so a plain mb reaches the instance:

mb db list
mb git-sync status

The mb profile

rde init logs mb in under default, the profile a plain mb uses. When mb has no default profile yet, or it already points at this instance, nothing is asked. When default points at another Metabase, init asks which profile to use, with the URL default points at today in the first choice:

  • default, replacing it (recommended; it points at <url> today): pre-selected, and what --yes and a run without a terminal take. init prints Replacing mb profile default, which pointed at <url>, so the old URL is on screen if you want to log it in again under another name (mb auth login --profile <name> --url <url>).
  • rde, and leave default alone: the instance gets the profile rde, and every command reads mb --profile rde <command>, or plain mb after export MB_PROFILE=rde.

--mb-profile default|rde, or "api-key": { "profile": "rde" } in the answers file, answers the question ahead of time. An instance not named default always uses rde-<name> and asks nothing, and an instance keeps the profile its state file records; the summary and rde status say which, with the command to use.

The git repository

With a license that includes remote sync, the last step connects a git repository. You choose where Metabase pushes, then where the working directory lives (here, another directory, or an existing repository):

  • Local remote inside ~/.rde (default): a bare repository at instances/default/data/content.git, mounted into the container and reached as file:///rde/content.git. No account, no token. Your working directory gets it as origin (or as rde-remote when origin already points elsewhere).
  • Hosted remote: a GitHub, GitLab, or Bitbucket https:// URL plus a token with read and write access. rde tests the connection through Metabase before saving anything, then clones the repository into the working directory (an existing repository gets the URL as origin instead); an empty hosted repository gets the first commit pushed to it. The token is handed to Metabase and, for the clone and that first push, to git in its environment; it never lands in the clone's config, the remote URL, or a credential helper, so a later git pull uses your own git credentials.

A fresh repository gets a first commit with a README.md and a .gitignore that keeps out what a data app built in it must not push: .env, .env.local, .env.*.local, node_modules/, .vite/, the rde skill's .scratch/, and .DS_Store (a data app's built dist/ stays tracked, since that bundle is what Metabase serves); a .gitignore already there keeps its lines and gets the missing ones, and a repository that already has commits is left as it is. A repository that already holds Metabase content is imported on the spot. Afterwards:

cd <your working directory>
mb git-sync status
mb git-sync export -b <branch> -m "what changed"   # init prints it with your branch
git pull   # after an export, to see the YAML files

Running init without a terminal

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.

Flags

rde init --skip-license \
         --database "postgres=postgres://user:pass@localhost:5432/app" \
         --git-dir ~/analytics --git-remote local

| Flag | Answers | | --- | --- | | --agents claude-code,codex / --skip-agents | Which agents get the skill, instead of asking at a terminal or taking the ones found on the machine | | --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-enterprise-head:latest, the latest release, and the first free port from 3100; --image implies docker and --jar implies jar; the sample database always runs in Docker | | --admin generated\|prompted, --admin-email, --admin-password | The admin user, generated unless given; an email or password implies prompted | | --license-token / --skip-license | The license | | --database <engine>=<connection string> (repeatable) / --skip-databases | Your own databases; --skip-databases connects none of them, and the sample database is still connected | | --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 | | --mb-profile default\|rde | The mb profile when default already points at another Metabase; default (replace it) unless given | | --yes | Every confirmation, and the recommended choice of a question (replacing default); skips the question about your own databases, and the sample database is still connected. A driver's toggle (SSL, an SSH tunnel) is a setting, not a confirmation, and keeps the driver's default |

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.

echo "$PASSWORD" | rde init --admin-email [email protected] --admin-password - …
rde init --license-token '$RDE_LICENSE_TOKEN' …

Answers file

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.

{
  "agents-skills": { "agents": ["claude-code"] },
  "runtime": { "image": "metabase/metabase-enterprise:latest", "port": 13000 },
  "admin-user": { "variant": "prompted", "email": "[email protected]", "password": "$RDE_ADMIN_PASSWORD" },
  "license": { "token": "$RDE_LICENSE_TOKEN" },
  "databases": [
    { "engine": "postgres", "uri": "$RDE_DATABASE_1_URI" },
    { "engine": "bigquery-cloud-sdk", "fields": { "project-id": "acme", "service-account-json": "/keys/acme.json" }, "name": "Warehouse" }
  ],
  "git-sync": { "location": "new", "path": "~/analytics", "remote": "local-bare" }
}

"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. A field that engine does not ask with the others given, such as one behind the admin form's advanced options, is named in a warning and ignored.

Recording a run

rde init --print-answers <file> writes, once an interactive run finishes, the answers file that reproduces it: the prompts as they were answered, each database as it was added (its connection string if used as pasted, otherwise its fields), 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 (a password, or an SSH private key). Set those variables and run rde init --answers <file> on the next machine.

Upgrading

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.

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.

rde upgrade                                              # Docker: pull the instance's image again; jar: the latest release
rde upgrade --to metabase/metabase-enterprise:v1.64.0    # Docker: a specific release
rde upgrade --to v1.64.0                                 # jar: a specific release
rde upgrade --to ~/metabase/target/uberjar/metabase.jar  # jar: a build of your own
rde status                                               # shows `Version: v1.64.0`

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.

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.

Resetting

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 (the one the state file records; never a profile pointing at another Metabase), and deletes the stored credentials and ~/.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.

rde uninstall                  # container or process, mb profile, ~/.rde/instances/default
rde uninstall --keep-data      # container or process and profile only; the data and secrets.json stay, and `rde init` reinstalls from them
rde uninstall --remove-skill   # also the rde skills from your coding agents
rde uninstall --remove-image   # also the Docker images or the downloaded jar (otherwise they stay for the next init)

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. A container labelled with another RDE_HOME is left alone, and the warning names that home. Anything it could not remove is listed at the end and the command exits with 1.

Troubleshooting

Start with rde doctor. It checks the machine (platform, Node, git, Docker, Java, every mb on PATH), the instance (the container or the jar process, port, health, Metabase version, admin user, admin password, whether Metabase accepts the API key, the secrets file's modes, no secret left in state.json, mb profile and the URL it points at, license, databases, the sample database's container, port, and Metabase record, remote sync), the MB_PROFILE, MB_URL, and MB_API_KEY variables when set, the other Metabases mb knows, the skill for each detected coding agent, and every copy of the skill, one row each with ✓, ✗, or – and a hint under every ✗. A row marked ! is a note: nothing fails, but it changes where mb goes. Of Docker and Java, the one the instance does not use is – when missing; the one it uses is ✗.

✓ container    rde-metabase-default stopped (metabase/metabase-enterprise-head:latest)
✓ port         3100 is free
✗ health       unreachable
               → `rde start`
– admin user   Metabase is not healthy

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 step that asks takes its recommended answer with --yes or without a terminal. 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.

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:

◇  This machine ──────────────────────────────────────────────────────────────────────────────────╮
│                                                                                                 │
│  ✓ macOS                                                                                        │
│  ✓ Node v22.13.1                                                                                │
│  ✓ git version 2.50.1                                                                           │
│  ✗ Docker: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker    │
│    daemon running?                                                                              │
│    Install Docker Desktop (https://www.docker.com/products/docker-desktop/), OrbStack, or       │
│    colima, and start it                                                                         │
│  ✗ Java: java is not installed                                                                  │
│    Install Java 21 or newer: brew install --cask temurin@21, or from https://adoptium.net       │
│                                                                                                 │
├─────────────────────────────────────────────────────────────────────────────────────────────────╯
Fix the items above and run rde init again

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.

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.

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.

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.

mb says the API key is invalid. The key was regenerated or revoked in Metabase's admin settings. rde doctor shows ✗ API key in the secrets file, but Metabase rejects it; rde init --only api-key (or rde doctor --fix) replaces it with the stored admin login and logs mb in again.

An older mb runs first. Another package manager installed an mb earlier, and its directory comes first on PATH. The mb row lists every mb in PATH order and fails when the first one is too old, or comes from another package manager while a newer one sits later; the hint names both paths and the command that removes the old one, taken from its path (bun remove -g @metabase/cli, brew uninstall <formula>, …), or the PATH order that puts the new one first. rde never edits shell profiles; open a new terminal after the change. rde init stops with the same list when the mb it installed is still not the one that runs.

mb went to the wrong Metabase. A plain mb uses the default profile, or the one MB_PROFILE names, and MB_URL or MB_API_KEY override every profile, the instance's own included. rde doctor shows a ! row for each of those variables that is set, with the unset to run, and lists every other profile by host with whether it authenticates and which one plain mb uses:

✗ mb profile       rde points at http://127.0.0.1:3101, not at this instance (http://localhost:3101)
                   → `rde init --only api-key` logs it back in against the instance
! MB_PROFILE       prod: `mb` and the rde skill will use profile prod (prod.acme.example) instead of rde's (rde)
                   → `unset MB_PROFILE`, remove it from your shell profile if it is exported there, then restart the agent session
✓ other Metabases  default: metabase.acme.example, not answering
                   staging: localhost:3101, login rejected
                   plain `mb` uses prod (MB_PROFILE): prod.acme.example

The agent follows an old copy of the skill. The rde skill copies row lists every copy of the skill the skills CLI knows, global and in the current directory's project, with where it came from, and fails when one did not come from rde's source (the branch is read from the skills lock file; against a local RDE_SKILL_SOURCE the copy's SKILL.md is compared with the checkout's skills/rde/SKILL.md). With several copies it says which one Claude Code started in that directory loads (a personal skill wins over a project one). rde init --only agents-skills reinstalls the global copy; restart the agent session afterwards.

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.