Try it
cookbookbeginner5 min readUpdated 2026-05-27

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

  1. The CLI binds a tiny HTTP listener on 127.0.0.1:0 (kernel picks a free port).
  2. It opens https://runtime-judgement-app.vercel.app/app/settings/api-keys?cli=1&port=PORT in your default browser.
  3. The settings page sees cli=1 and shows a "CLI login pending" panel with a one-click Authorize button.
  4. Click it. The page mints a fresh rj_live_... token and POSTs it to http://127.0.0.1:PORT/token.
  5. The CLI's listener receives the token, writes it to ~/.rj/token, replies "Login successful" to the browser tab, and exits.
  6. The CLI then runs the same connectivity check rj whoami runs — 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 with 0700 (owner rwx, nothing else).
  • File: ~/.rj/token — one line, no trailing newline, mode 0600 (owner rw, nothing else).
  • Existing dir with looser perms is tightened to 0700.
  • On Windows the chmod calls 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

Related articles

Try it