auth · git:20260831.4d8f7f6 · 2026-08-31 · sha256 879b69d6aa1fd633
auth git:20260831.4d8f7f6A
Immutable. This exact content is served forever at /api/v1/blob/879b69d6aa1fd633.
---
name: auth
description: "Sign in to Mubit and store an API key for this machine. Use on a fresh install, when the plugin reports auth_failed, or when a key has been rotated or revoked."
disable-model-invocation: false
allowed-tools: ["Bash(node ${CLAUDE_PLUGIN_ROOT}/bin/auth.mjs:*)"]
---
**This skill never installs anything.** It opens a browser, receives a key, checks it against
the instance, and writes it to one file. It does not install packages, start services, or touch
the user's shell profile. A memory plugin that runs installers is a trust failure.
## What it produces
Two values, which are the whole of the plugin's setup:
| Setting | Value |
| --- | --- |
| `endpoint` | the user's Mubit instance URL, e.g. `https://api.mubit.ai` |
| `apiKey` | a key of the form `mbt_...` |
They land in `${CLAUDE_PLUGIN_DATA}/credentials.json`, owner-only (mode 600). That path
survives plugin updates, so this is a once-per-machine step, not a once-per-release one.
**`--data-dir "${MUBIT_CC_DATA_DIR:-${CLAUDE_PLUGIN_DATA}}"` is not optional, on any of the
commands below.** A Bash tool call does not inherit `CLAUDE_PLUGIN_DATA` — it arrives empty —
so without the flag the command has to guess which of several `mubit-memory*` directories the
host is using, and a key written to the wrong one is indistinguishable, from the user's side,
from a sign-in that never happened. The host substitutes `${CLAUDE_PLUGIN_DATA}` into this file
before you read it, and the shell default around it lets a session that pins
`MUBIT_CC_DATA_DIR` keep its pin — the same order every hook and the MCP launcher resolve in,
so the sign-in lands where they read. Pass the whole expression straight through. Do not turn
it into an `ENV=… node …` prefix: that is no longer a `node` command and will stop for a
permission prompt.
## Step 1 — run it
**Say what is about to happen before you run it.** This command opens a browser and then
blocks with no output until it finishes — a Bash tool call does not stream, so there is
nothing to print in the meantime. Tell the user: a browser tab is opening; sign in or create
an account there; a first-ever workspace takes a minute or two to provision and the page
waits for it; then come back here.
**Run it with a Bash `timeout` of `600000`.** The default is 120000, and the harness kills the
command at that point — in the middle of a sign-up that was going fine.
```bash
node "${CLAUDE_PLUGIN_ROOT}/bin/auth.mjs" --data-dir "${MUBIT_CC_DATA_DIR:-${CLAUDE_PLUGIN_DATA}}" --json
```
This opens the Mubit console in a browser. The user signs in (or signs up — the same page does
both), and the key comes back over a loopback callback on `127.0.0.1`.
**Prefer this over asking the user to paste a key.** A key pasted into the conversation is in
the transcript, and transcripts get shared, exported, and attached to bug reports. In the
browser flow the key never passes through the conversation at all.
## Step 2 — read the exit code
| Exit | Meaning | What to tell the user |
| --- | --- | --- |
| `0` | Signed in, and the key was checked against the instance. | Report the endpoint, then go to step 3. |
| `2` | Still in flight — the workspace is provisioning, or the sign-up did not finish inside the deadline. **Not a failure.** | "Your workspace is still being created — usually a minute or two. Run `/mubit-memory:auth` again shortly; it picks up where it left off." Do not change anything, and do not offer the paste route: nothing is broken. |
| `1` | Something went wrong. The `state` field says which. | See the table below. |
On exit `1`, `state` is one of:
| `state` | What it means | What to tell the user |
| --- | --- | --- |
| `browser_failed` | **No browser could be opened at all.** Not a timeout — a timeout after a browser opened is exit `2`. | Fall through to step 2b. This is the common case over SSH and in containers. |
| `auth_failed` | The instance rejected the key. | The key is wrong, revoked, or issued for a different instance. Issue a new one in the console. |
| `unreachable` | Nothing answered at the endpoint. | The endpoint is wrong, or the instance is not running. This is not a key problem — do not have them reissue one. |
| `server_error` | The instance is up and failing. | Retry once, then check the instance in the console. The client cannot fix it. |
| `invalid_key` | What was supplied is not `mbt_`-shaped. | A truncated paste. Ask for the whole key. |
### Step 2b — no browser
Ask the user to issue a key at <https://console.mubit.ai>, then run it with the key in the
environment for that one command:
```bash
MUBIT_AUTH_KEY='mbt_…' node "${CLAUDE_PLUGIN_ROOT}/bin/auth.mjs" --data-dir "${MUBIT_CC_DATA_DIR:-${CLAUDE_PLUGIN_DATA}}" --paste --json
```
The key goes in the environment rather than in `--key` because a process's arguments are
readable by every user on the machine, and its environment is not. Never put the key in a
command-line flag, and never echo it back.
## Step 3 — the session boundary
`/reload-plugins` registers the hooks but does not fire `SessionStart`, so until the user starts
a **new** session there is no run id, no registered agent, and nothing on the status line. This
is the single most common "it's still broken" report immediately after a successful sign-in, and
it is not a fault. Say it explicitly.
## Related
- `/mubit-memory:setup` — reads what is configured and confirms the instance answers. Run it
after this to see `ready`.
- `node "${CLAUDE_PLUGIN_ROOT}/bin/auth.mjs" --data-dir "${MUBIT_CC_DATA_DIR:-${CLAUDE_PLUGIN_DATA}}" --status` — what is stored right now. Reports
whether a key is present, never the key itself. Exits non-zero when nothing is configured.
- `node "${CLAUDE_PLUGIN_ROOT}/bin/auth.mjs" --data-dir "${MUBIT_CC_DATA_DIR:-${CLAUDE_PLUGIN_DATA}}" --logout` — removes the stored credentials.
If the user already has `apiKey` set through `/plugin` → Mubit Memory → configure, or exports
`MUBIT_API_KEY`, both of those win over what this skill writes. That is deliberate: a deliberate
per-install setting and a CI environment variable should not be silently overridden by a login.
Say so rather than authenticating again in a loop.