robotrace login

A browser-driven sign-in for the Python SDK. Instead of minting an API key in the portal, copying the plaintext, and pasting it into ROBOTRACE_API_KEY, you run one CLI command. The SDK takes care of the round-trip and saves a credentials file your scripts pick up automatically.

$ robotrace login
Welcome to RoboTrace (https://app.robotrace.dev).
Logging in this machine via your browser…
 
To authorize this device, open:
  https://app.robotrace.dev/cli/auth?code=XKDF-PQ4N
 
Confirmation code: XKDF-PQ4N
Make sure the same code shows up on the page before you click Authorize.
 
Waiting for browser approval...
 
 Logged in as art@robotrace.dev.
  Credentials saved to ~/.robotrace/credentials (profile: default).
  Portal: https://app.robotrace.dev/portal

The next time you run a script that uses robotrace, no env vars are needed:

import robotrace as rt
 
ep = rt.start_episode(name="pick_and_place v3")
# [robotrace] → https://app.robotrace.dev/portal/episodes/ep_8f3c
ep.upload_video("./run.mp4")
ep.finalize(status="ready")

That [robotrace] → line is a clickable link in modern terminals (iTerm2, the macOS Terminal, Alacritty, Wezterm, VS Code, JetBrains). Cmd-click takes you straight to the new episode's portal page.

How it works

robotrace login implements a device-code flow modelled on RFC 8628:

  1. The CLI hits POST /api/cli/auth/start and gets back two tokens — a device_code (its bearer secret) and a user_code (the human-readable confirmation string).
  2. The CLI prints the verification URL with the user_code embedded, and tries to open your default browser there. You can pass --no-browser to skip the auto-open if you're SSH'd into a headless box and want to copy the URL by hand.
  3. You sign in on the portal as usual, then land on /cli/auth. The page shows the same user_code plus the device fingerprint the CLI sent (your hostname, IP, user-agent). If it all checks out, you click Authorize device.
  4. The portal mints a fresh API key for your account and stamps the approval onto the device session.
  5. The CLI's next poll picks up the key, writes it to ~/.robotrace/credentials, and exits.

The plaintext key sits in the database for at most a few seconds — the polling endpoint nulls it the instant it hands it back, and the session row's status moves to consumed so the same device_code can never be redeemed twice.

What gets saved

# ~/.robotrace/credentials
# managed by `robotrace login` — do not commit.
 
[default]
api_key    = "rt_…"
base_url   = "https://app.robotrace.dev"
client_id  = "<uuid>"
user_email = "art@robotrace.dev"
written_at = "2026-05-04T20:08:14Z"

The file is written with mode 0600, in a directory chmod'd to 0700. Don't commit it. Add .robotrace/ to your global .gitignore if you keep dotfiles in a repo:

echo ".robotrace/" >> ~/.gitignore_global

Each profile is one logged-in deployment. Most teams only need default; if you talk to staging and production from the same laptop, run:

robotrace login --profile staging --base-url https://staging.robotrace.dev
robotrace login --profile prod    --base-url https://app.robotrace.dev

The Python SDK reads default automatically. To target a different profile in code, pass api_key= and base_url= explicitly — full profile-aware loading is on the roadmap.

Resolution order

When you import robotrace, the SDK resolves credentials in this order:

  1. Explicit kwargs — Client(api_key=..., base_url=...)
  2. Environment variables — ROBOTRACE_API_KEY, ROBOTRACE_BASE_URL
  3. The default profile from ~/.robotrace/credentials
  4. Otherwise: ConfigurationError

This means CI machines that already inject env vars don't need to run robotrace login — and if you change a key in the portal, the env var still wins until you remove it.

whoami

Prints which deployment + email the saved profile is for, without exposing the key:

$ robotrace whoami
Profile:    default
User:       art@robotrace.dev
Client ID:  9f8e7d…-c1a2
Base URL:   https://app.robotrace.dev
Saved at:   2026-05-04T20:08:14Z
File:       /Users/art/.robotrace/credentials

Add --json if you want to pipe it into a CI report.

logout

Removes the saved profile from your machine:

$ robotrace logout
 Removed profile 'default' from ~/.robotrace/credentials.
Note: the API key minted for that login is still valid until you
revoke it from the portal API keys.

logout does not revoke the underlying API key on the server — that's intentional, so you can move credentials between machines without invalidating the key. To kill the key for good, head to Portal → API keys and click Revoke next to the row labelled robotrace-cli (XKDF-PQ4N).

Why not just paste an API key?

Because the first thing every developer evaluating the SDK does is type something into a terminal. If that something is "follow this link, click a button, watch the URL print itself," the SDK feels alive. If it's "go to the portal, click around, copy a 47-character secret, paste it into .env.local, hope you didn't typo," the SDK feels like a side project.

Both flows still work — the admin can mint a key the old-fashioned way for CI environments — but robotrace login is the one we'd rather you start with.

Troubleshooting

"Login window expired before the device was authorized" — the flow has a 10-minute TTL. Run robotrace login again.

"Device login was refused" — somebody (probably you, in the other tab) clicked Refuse on the verification page. No state change on the server side; safe to re-run.

The browser didn't open — pass --no-browser and copy the URL by hand. Or set BROWSER= in your environment if you have a preferred browser opener.

No ~/.robotrace/credentials after a successful login — check $ROBOTRACE_HOME. If it's set, the file lives at $ROBOTRACE_HOME/credentials instead. We respect the override so tests and CI runs can isolate from the user's real config.