Log the rj CLI in
I want to use rj from my laptop without copy-pasting tokens. rj login opens a browser, mints a fresh token, and writes it to ~/.rj/token with the right perms — or falls back to paste mode for headless / SSH sessions.
Log the rj CLI in
The Sprint 14 CLI ships 5 read commands that work against any RJ deployment, but until Sprint 15 you had to copy a rj_live_... token out of the API-keys page and either paste it into an env var on every shell or hand-write ~/.rj/token. rj login collapses that to a single command. It opens a browser, the settings page mints a fresh token and POSTs it back to a one-shot listener on 127.0.0.1, the CLI writes the token to ~/.rj/token with 0600 perms, then runs rj whoami so you know immediately whether the new token actually works.
For SSH sessions or headless boxes where you can't open a browser, rj login --paste skips the listener and reads a pasted token from stdin instead.
Setup
You need the CLI on PATH. Today (Sprint 15) that's the local build:
git clone https://github.com/rambo-01/runtime-judgement-app
cd runtime-judgement-app
pnpm install
pnpm --filter @runtime-judgement/cli build
alias rj="node $(pwd)/packages/rj-cli/dist/index.js"
Or wait for pnpm add -g @runtime-judgement/cli once we publish (tracked in dev-docs/runbooks/2026-05-22-mcp-publish.md).
If RJ_AUTH_TOKEN is already set in the environment, rj login still writes to ~/.rj/token — the env var takes precedence at read time, so once you've logged in you can unset RJ_AUTH_TOKEN and everything keeps working.
Browser flow (default)
rj login
What happens, step by step (packages/rj-cli/src/commands/login.ts):
- The CLI binds a tiny HTTP listener on
127.0.0.1:0(kernel picks a free port). - It opens
https://runtime-judgement-app.vercel.app/app/settings/api-keys?cli=1&port=PORTin your default browser. - The settings page sees
cli=1and shows a "CLI login pending" panel with a one-click Authorize button. - Click it. The page mints a fresh
rj_live_...token and POSTs it tohttp://127.0.0.1:PORT/token. - The CLI's listener receives the token, writes it to
~/.rj/token, replies "Login successful" to the browser tab, and exits. - The CLI then runs the same connectivity check
rj whoamiruns — so you immediately know whether the token actually authenticates.
You should see:
rj login
base URL https://runtime-judgement-app.vercel.app
callback listener on http://127.0.0.1:54391
opening https://runtime-judgement-app.vercel.app/app/settings/api-keys?cli=1&port=54391
waiting for browser…
wrote token to /Users/you/.rj/token
verifying…
rj
base URL https://runtime-judgement-app.vercel.app
token rj_live_abcd…wxyz
OK
rj login: success.
Once that lands, rj whoami, rj agents list, and the other 4 commands resolve the token from ~/.rj/token automatically.
Paste flow (headless / SSH)
rj login --paste
Skip the browser entirely. The CLI prompts:
Paste mode — generate a token at https://runtime-judgement-app.vercel.app/app/settings/api-keys, then paste it below.
Generate a token on your laptop's browser, paste it into the SSH session (echo is off so the token doesn't surface in the scrollback). Same ~/.rj/token write + rj whoami verify as the browser flow.
Use --paste for:
- SSH sessions (no local browser to call back to)
- CI bootstrap (one-time setup of a service-account token)
- Locked-down environments where the listener can't bind
- Anywhere you'd rather not grant a local listener access to your browser
Token storage
writeTokenFile() in packages/rj-cli/src/login/token-file.ts follows the ssh / gh auth convention:
- Directory:
~/.rj/— created with0700(owner rwx, nothing else). - File:
~/.rj/token— one line, no trailing newline, mode0600(owner rw, nothing else). - Existing dir with looser perms is tightened to
0700. - On Windows the
chmodcalls are best-effort no-ops (FAT/exFAT) — that's the platform's problem; the CLI doesn't refuse to write the file.
cat ~/.rj/token should show a single rj_live_... string. If you accidentally lose the file, rj login again — it's the same token from your perspective.
Multi-environment
For staging, self-hosted, or per-tenant deployments, override the base URL:
# One-shot
rj login --base-url https://staging.example.com
# Or via env (persists across login + every subsequent command)
export RJ_BASE_URL=https://staging.example.com
rj login
rj login resolves the base URL in this order: --base-url flag → RJ_BASE_URL env var → default (https://runtime-judgement-app.vercel.app). The token file is shared across environments — if you flip RJ_BASE_URL after logging into prod, the same token is sent to the staging API, which will probably 401. Log in again per environment, or keep separate tokens in per-env env vars (RJ_AUTH_TOKEN).
Pitfalls
Port conflict
startCallbackServer({ port: 0 }) asks the kernel for a free ephemeral port. If your network namespace blocks all of those, the listener errors at start; the CLI prints the error and exits non-zero. Retry, or use --paste.
Browser blocks 127.0.0.1
Some corporate browsers block http://127.0.0.1:* for security. The browser tab will fail to POST the token; the listener times out after 5 minutes (configurable with --timeout-ms). The error message points you at --paste as the fallback.
--no-open for headless boxes that DO have a local listener
If you want the callback flow but can't auto-open a browser (e.g. SSH + X-forwarding off but a tunnel exposing the listener):
rj login --no-open
The CLI prints the URL instead of trying to spawn xdg-open / open / start. Paste it into your local browser; the callback still hits your remote listener.
Token immediately 401s
The connectivity check runs after every successful write. A 401 here means the minted token isn't valid — most often because the Clerk session that minted it has expired between the redirect and the callback. Re-run rj login (the token-mint endpoint will accept a fresh session). If the second attempt also 401s, file an issue with the response body — the JSON-RPC error envelope is the truth source.
~/.rj/token exists but rj whoami still says "OK" for a stale account
The CLI loads in this order: RJ_AUTH_TOKEN env → ~/.rj/token. If you've left a stale env var around, rj login won't use the new token until you unset RJ_AUTH_TOKEN. rj whoami shows which token (truncated) is actually loaded.
Next steps
- The five commands the new token unlocks: Use the rj CLI
- The MCP write surface the same token authenticates against: Automate with MCP write tools
- Same primitives from your editor (also token-authed): Use RJ from Claude Code via MCP
- The backlog item that drove this work: C-6 in
dev-docs/backlog.md— "rj login(token-flow CLI auth)"