# Authentication and setup (/sling-cli/commands/auth)



Five commands cover getting `sling` working and confirming it still is.

## `sling login`

```text
sling login [--force] [--clear]
```

Signs in with the **GitHub device-code flow**: requests a code, opens the approval page, waits for you to approve in the browser, saves the credential to `~/.config/sling/credentials`, then makes a best-effort attempt to set your default org. If you are already signed in it short-circuits — verifying the token still works rather than re-running the flow.

The device-code flow is deliberate: it needs no localhost callback, so you can sign in from any terminal, including over SSH.

| Flag                    | Meaning                                                               |
| ----------------------- | --------------------------------------------------------------------- |
| `--force`               | Re-authenticate even when a valid session exists.                     |
| `--clear`               | Clear the stored credential; an alias for `logout --yes`.             |
| `--agent`, `--no-input` | Refuse: the device-code flow cannot run non-interactively (exit `2`). |

```console
$ sling login
Opened https://runners.starsling.dev/cli-login?user_code=AB58-LRFS in your browser.
Confirm code AB58-LRFS to continue.
Waiting for you to approve…

Sling CLI v0.1.0
✓ Successfully authenticated!
✓ Token saved to ~/.config/sling/credentials
✓ You're now linked to acme organization!
```

<Callout type="info">
  An agent on this machine inherits the credential this writes, so signing in once covers both of you. A fresh CI container has no session and no browser: see [how agents authenticate](/sling-cli/configuration#how-agents-authenticate).
</Callout>

## `sling logout`

```text
sling logout [--yes]
```

Clears the stored credential **and the persisted default org**, so a stale org cannot leak into the next login under a different account. On a terminal it asks first.

| Flag         | Meaning                                                                              |
| ------------ | ------------------------------------------------------------------------------------ |
| `--yes`      | Skip the confirmation — explicit consent to clear.                                   |
| `--agent`    | Machine-mode consent; bundles `--yes`.                                               |
| `--no-input` | **Not** consent. Fails with exit `2` rather than prompting when a credential exists. |

```console
$ sling logout
Are you sure you want to sign out? (y/N) y

✓ Successfully signed out!
✓ Token cleared from ~/.config/sling/credentials
```

## `sling whoami`

```text
sling whoami [--json]
```

Your identity, org, plan, token scopes, and token expiry — so you can debug access issues without guessing. Session callers see the default org enriched with its plan; API-key callers also see the key id and its scopes.

| Flag                | Meaning                                                                 |
| ------------------- | ----------------------------------------------------------------------- |
| `--json`, `--agent` | Machine output — the `/api/whoami` payload on stdout, errors on stderr. |

```console
$ sling whoami

Identity
  Name          Lionel Messi
  GitHub        leo_messi
  Email         leomessi@example.com
  User ID       leo_messi_10
Organization
  Default       arg
  Plan          enterprise
Credential
  Type          session
  Expires       1 Aug 2026, 00:00
```

The human table renders on stderr, so stdout stays pipe-clean. Calls [`GET /api/whoami`](/api/identity/get-whoami).

## `sling doctor`

```text
sling doctor [--json]
```

Diagnoses a broken setup in one command: **binary version, token validity and scopes, org resolution, git remote detection, control-plane reachability, clock skew, and patch-tooling presence**. Exit `0` when healthy, **`10`** when a real check fails — so an agent harness can preflight the environment before starting work.

A check whose source is not wired yet is **skipped** (`○`) and never fails the run.

| Flag                | Meaning                                                                                 |
| ------------------- | --------------------------------------------------------------------------------------- |
| `--json`, `--agent` | Machine output — `{ checks: [{ key, ok, detail, skipped?, fix_command? }] }` on stdout. |

```console
$ sling doctor --json
{
  "checks": [
    { "key": "token",         "ok": true,  "detail": "valid session (expires 4 Aug 2026, 17:45)" },
    { "key": "control_plane", "ok": true,  "detail": "reachable (https://runners.starsling.dev)" },
    { "key": "clock_skew",    "ok": true,  "detail": "0s vs server" },
    { "key": "git_remote",    "ok": true,  "detail": "origin" },
    { "key": "patch_tooling", "ok": true,  "detail": "git found (/usr/bin/git)" },
    { "key": "version",       "ok": false, "skipped": true, "detail": "on 0.1.0", "fix_command": "sling update" },
    { "key": "org",           "ok": true,  "detail": "starslingdev (paid)" }
  ]
}
```

A real failing check carries a `fix_command` and flips the exit code to `10`. A skipped check — `ok: false` with `skipped: true` — is informational and keeps the run healthy.

## `sling org switch`

```text
sling org switch [slug]
```

Sets your **default org** so later commands resolve it without a flag. Pass a slug to persist it directly, or omit it on a terminal for an arrow-key picker. An unknown slug exits `2` and lists your valid orgs.

| Flag                | Meaning                                                                         |
| ------------------- | ------------------------------------------------------------------------------- |
| `slug`              | The org to make default. Omit for the interactive picker.                       |
| `--json`, `--agent` | Machine JSON — `{"defaultOrg":"…"}` on stdout. A missing slug is a usage error. |
| `--no-input`        | Refuse the picker when no slug is given (exit `2`).                             |

```console
$ sling org switch beta
You're now linked to beta organization!

# no slug on a TTY → picker
$ sling org switch
Select your default org (↑/↓, Enter; Esc to cancel):
❯ acme
  beta
```

<Callout type="info">
  Your org is auto-resolved when it is unambiguous, so this is only needed on multi-org accounts — the point is that they do not pay a flag tax on every call. Either way it is a CLI convenience: the HTTP API never infers an org, and [`org` is a required parameter](/api/credentials#org-scoping) on every org-scoped endpoint. Use [`GET /api/orgs`](/api/identity/list-orgs) to list the slugs you can use.
</Callout>
