# Auth reference

> GitHub OAuth device flow (`ok auth login`), PAT storage, `gh` CLI credential reuse, git credential helper, and token scopes for clone, sync, and share operations.

- Repository: sashimikun/open-knowledge
- GitHub: https://github.com/sashimikun/open-knowledge
- Human docs: https://grok-wiki.com/public/docs/sashimikun-open-knowledge-5c45105c876e
- Complete Markdown: https://grok-wiki.com/public/docs/sashimikun-open-knowledge-5c45105c876e/llms-full.txt

## Source Files

- `packages/cli/src/commands/auth/index.ts`
- `packages/cli/src/commands/auth/login.ts`
- `packages/cli/src/commands/auth/pat.ts`
- `packages/cli/src/commands/auth/git-credential.ts`
- `packages/cli/src/commands/auth/status.ts`
- `packages/server/src/github-permissions.ts`
- `packages/cli/src/commands/clone.ts`

---

---
title: "Auth reference"
description: "GitHub OAuth device flow (`ok auth login`), PAT storage, `gh` CLI credential reuse, git credential helper, and token scopes for clone, sync, and share operations."
---

Open Knowledge authenticates against **GitHub** and **GitHub Enterprise Server (GHE)** only. Credentials power `git` operations (clone, pull, push, sync) and GitHub REST API calls (repo listing, publish, push-permission probes). The CLI resolves credentials in a fixed priority order, stores OAuth tokens when you sign in through Open Knowledge, and wires them into `git` through a credential helper.

## Credential tiers

Open Knowledge classifies the active credential source as tier **A**, **B**, **C**, or **none**. Resolution is consistent across `ok auth status`, `ok clone`, and server-side sync.

| Tier | Source | Git credential helper | Disconnect in app |
| --- | --- | --- | --- |
| **A** | `gh` CLI (`gh auth token`) | `credential.helper=!gh auth git-credential` | Not applicable — managed by `gh` |
| **B** | Token stored by Open Knowledge (`gitProtocol: https`) | `credential.helper=!open-knowledge auth git-credential` | `ok auth signout` or Settings → Disconnect |
| **C** | Token stored with `gitProtocol: ssh` (status classification only) | Same helper as tier B when used for HTTPS remotes | Same as tier B |
| **none** | No credential | No helper injected | — |

Tier **A** always wins when `gh` is installed and returns a non-empty token. A stored Open Knowledge token is ignored until `gh` stops providing one.

<Callout type="info">
  On the desktop app, Settings → Account shows **Connected via gh CLI** for tier A and exposes **Disconnect** only for tiers B and C. Disconnecting clears Open Knowledge's stored token, not `gh`'s session.
</Callout>

## Sign in with device flow

`ok auth login` runs the GitHub OAuth **device authorization flow**. It is the primary sign-in path when `gh` is unavailable or you want Open Knowledge to own the token.

<Steps>

<Step title="Run login">

```bash
ok auth login
```

For GHE, pass the hostname:

```bash
ok auth login --host github.example.com
```

</Step>

<Step title="Authorize in the browser">

The CLI prints a verification URL and one-time code:

```
Open: https://github.com/login/device
Enter code: XXXX-XXXX
```

Complete authorization in the browser before the code expires.

</Step>

<Step title="Verify">

```bash
ok auth status
```

Expected output when authenticated:

```
✓ Logged in as <login> on github.com
```

</Step>

</Steps>

### JSONL mode

Pass `--json` for machine-readable progress events (used by the editor and desktop app via `/api/local-op/auth/login`):

<RequestExample>

```bash
ok auth login --json
```

</RequestExample>

<ResponseExample>

```jsonl
{"type":"verification","user_code":"ABCD-1234","verification_uri":"https://github.com/login/device","expires_in":899}
{"type":"complete","host":"github.com","login":"octocat"}
```

</ResponseExample>

### OAuth scopes

Device flow requests these scopes by default:

| Scope | Purpose |
| --- | --- |
| `repo` | Clone, pull, and push private repositories; create repos during publish |
| `read:user` | Resolve the authenticated login after sign-in |
| `user:email` | Attach name and email metadata to the stored credential |

### OAuth client ID

The bundled OAuth app client ID is used unless overridden:

<ParamField body="OPEN_KNOWLEDGE_GITHUB_CLIENT_ID" type="string">
  Optional environment variable. When unset, Open Knowledge uses the default client ID from `@inkeep/open-knowledge-core`.
</ParamField>

## Store a personal access token

Use `ok auth pat` when device flow is unavailable, when you need a token with explicit scopes, or when clone fails with a **scope mismatch**.

<Steps>

<Step title="Create a PAT on GitHub">

Create a classic PAT or fine-grained token with at least the **`repo`** scope (full control of private repositories). For publish flows that create repositories, `repo` is required.

</Step>

<Step title="Store it in Open Knowledge">

```bash
ok auth pat
```

The command prompts securely, validates the token against `GET /user`, then stores it with `gitProtocol: https`.

</Step>

<Step title="Confirm">

```bash
ok auth status --json
```

</Step>

</Steps>

`ok auth pat` supports `--host` for GHE and `--json` for scripting.

## Reuse `gh` CLI credentials

When `gh` is on `PATH` and `gh auth token` returns a token, Open Knowledge treats that as **tier A** and delegates all `git` HTTPS authentication to:

```
credential.helper=!gh auth git-credential
```

`detectGh` probes `gh` and common install paths (`/opt/homebrew/bin/gh`, `/usr/local/bin/gh`, `/usr/bin/gh`, and others) with a 5-second timeout.

If you already use `gh auth login`, you typically do not need `ok auth login` for clone or sync on the same machine.

## Token storage

Stored credentials (tiers B and C) persist per GitHub host.

| Backend | Location | When used |
| --- | --- | --- |
| **keyring** (preferred) | OS keychain, service `open-knowledge`, account = host | Default on macOS and supported Linux desktops |
| **file** (fallback) | `~/.ok/auth.yml` (mode `0600`, directory mode `0700`) | Headless Linux, keychain timeout (>2s init), or keychain unavailable |

On first read, a file-stored credential is **migrated** into the keychain when possible; the plaintext copy is removed.

Each entry stores:

<ResponseField name="login" type="string">
  GitHub username from `GET /user`.
</ResponseField>

<ResponseField name="token" type="string">
  OAuth or PAT bearer token used as the git HTTPS password.
</ResponseField>

<ResponseField name="gitProtocol" type="string">
  `https` (default after login or PAT) or `ssh` (affects tier classification).
</ResponseField>

<ResponseField name="name" type="string">
  Display name from the GitHub profile, when available.
</ResponseField>

<ResponseField name="email" type="string">
  Primary email from the GitHub profile, when available.
</ResponseField>

`ok auth signout --host <host>` clears the credential from **both** backends when present.

## Git credential helper

Open Knowledge implements the [git credential protocol](https://git-scm.com/docs/gitcredentials) as a hidden subcommand:

```bash
open-knowledge auth git-credential get
```

`git` invokes this helper (via `credential.helper=!open-knowledge auth git-credential`) during HTTPS clone, pull, and push. The helper:

1. Reads `host=` (and related fields) from stdin.
2. Looks up a stored token for that host.
3. Writes `username=<login>` and `password=<token>` to stdout, or exits `1` when absent.

### `gh` token relay

When the collaboration server runs `git` with a tier-A `gh` token, it sets environment variables consumed by the helper:

| Variable | Role |
| --- | --- |
| `OK_GH_TOKEN` | Bearer token to relay |
| `OK_GH_TOKEN_HOST` | Host the token applies to |

If both are set and `OK_GH_TOKEN_HOST` matches the requested host, the helper returns `username=x-access-token` and the relayed password — without reading the keychain. This lets sync reuse `gh` credentials even though the persistent helper path is `open-knowledge auth git-credential`.

### Clone and sync wiring

| Operation | Credential resolution |
| --- | --- |
| `ok clone` | `resolveAuth` → tier A/B helper, or skip auth for **public** `github.com` HTTPS repos |
| `ok sync` / `ok push` / `ok pull` | Uses the project's existing `git` remote and system/git config; server auto-sync injects `buildSyncCredentialArgs` (`credential.helper=!open-knowledge auth git-credential`) |
| Collaboration server sync engine | Same helper plus optional `OK_GH_TOKEN` relay from `createGhTokenSource` (60s TTL cache) |

Clone sets `GIT_TERMINAL_PROMPT=0` so `git` never blocks on interactive username/password prompts.

## Auth commands

| Command | Description |
| --- | --- |
| `ok auth login` | Device flow sign-in; stores token in keychain/file |
| `ok auth pat` | Prompt for and store a PAT |
| `ok auth status` | Show login and tier; exits `1` when unauthenticated |
| `ok auth repos` | List repositories visible to the resolved token |
| `ok auth signout` | Remove stored credentials for `--host` |
| `ok auth git-credential get` | Git credential helper (not for direct use) |

Shared flags:

<ParamField body="--host" type="string" default="github.com">
  GitHub.com or GHE hostname. Non-GitHub hosts (GitLab, Bitbucket, etc.) are rejected.
</ParamField>

<ParamField body="--json" type="boolean" default="false">
  Emit JSON or JSONL instead of human-readable stderr.
</ParamField>

### Status JSON shape

```json
{
  "type": "status",
  "host": "github.com",
  "backend": "keyring",
  "authenticated": true,
  "tier": "B",
  "login": "octocat",
  "name": "The Octocat",
  "email": "octocat@github.com"
}
```

## Scopes and operations

### Clone (`ok clone`)

| Repo visibility | Auth required? | Minimum scope |
| --- | --- | --- |
| Public on `github.com` | No — anonymous HTTPS clone | — |
| Private or non-`github.com` public | Yes | `repo` (HTTPS) or SSH key (SSH URL) |

On auth failure, `ok clone` prints actionable recovery:

- **No credential / 401 / unknown auth** → `ok auth login`, then re-run clone
- **Scope mismatch** (`insufficient scopes`, `missing scope`, `required scope`) → create PAT with `repo`, run `ok auth pat`, re-run clone
- **403 with access** → check repository permissions for the signed-in account
- **SSH auth failure** → fix SSH keys or switch to HTTPS URL

### Sync (`ok sync`, auto-sync)

Sync performs `git pull` and `git push` against `origin`. It needs the same HTTPS credentials as manual git, effectively requiring **`repo`** scope for private remotes.

The sync engine surfaces typed error codes including `auth-401`, `auth-403`, `auth-scope-mismatch`, and `auth-no-credential`. When credentials expire or lack scope, sync enters **`auth-error`** state until you reconnect.

Push-permission probes (`checkPushPermission`) call `GET /repos/{owner}/{repo}` and read `permissions.push`. Token resolution order: **`gh` token → token store → anonymous (denied)**.

### Share and publish

Two distinct surfaces use GitHub auth:

| Surface | Commands | Auth use |
| --- | --- | --- |
| **Publish to GitHub** | `ok share owners`, `ok share name-check`, `ok share publish` | REST API to create repo + `git push` for initial upload |
| **Config sharing mode** | `ok config-sharing share`, `unshare`, `status` | Local `.git/info/exclude` only — **no GitHub token** |

Publish resolves tokens via the same order as `ok auth repos`: **`gh` first, then stored token**. Without a token, publish returns `auth-required`.

`ok share publish` requires `--owner`, `--name`, `--visibility`, and `--project-dir`. It creates the repository (user or org), sets `origin`, and pushes.

## GitHub Enterprise Server

Pass `--host <ghe-hostname>` to auth commands. API calls use `https://<host>/api/v3`. Device flow and PAT validation respect the host-specific base URL.

`validateGitHubHost` rejects known non-GitHub forge hostnames at the CLI layer.

## Desktop and editor integration

The desktop utility process passes `detectGh` and `makeLazyProbeTokenStore()` into the collaboration server so sync and permission probes share the same credential model as the CLI.

Editor flows:

- **Settings → Account → Connect GitHub** spawns `ok auth login --json` through the local-op HTTP API.
- **Disconnect** spawns `ok auth signout`.
- **Clone / publish wizards** call `ok auth status` and `ok auth repos` subprocesses.

Tier A accounts show as managed by `gh`; disconnect is not offered because Open Knowledge does not store a separate token.

## Troubleshooting

<AccordionGroup>

<Accordion title="Not logged in during clone">

```bash
ok auth login
ok clone owner/repo
```

If `gh` is authenticated, check `gh auth status` — tier A should satisfy private clones without `ok auth login`.

</Accordion>

<Accordion title="Token invalid after sign-in">

```bash
ok auth status
```

If status reports `token invalid`, sign out and sign in again:

```bash
ok auth signout
ok auth login
```

</Accordion>

<Accordion title="Scope mismatch on push or clone">

Create a PAT with **`repo`** scope at [github.com/settings/tokens](https://github.com/settings/tokens), then:

```bash
ok auth pat
```

Re-run the failing command. Scope-mismatch recovery intentionally suggests `ok auth pat`, not another device-flow login.

</Accordion>

<Accordion title="Keychain fallback to file storage">

If you see `[auth] token storage: file (~/.ok/auth.yml)`, the OS keychain was unavailable or slow to initialize. Credentials still work; permissions on `~/.ok/auth.yml` are restricted to your user.

</Accordion>

<Accordion title="Sync paused with auth-error">

Reconnect via Settings → Account or `ok auth login`. Confirm `ok auth status` succeeds, then trigger sync with `ok sync` or the editor sync control.

</Accordion>

</AccordionGroup>

## Related pages

<CardGroup cols={2}>

<Card title="GitHub sync and conflicts" href="/github-sync-and-conflicts">
  Clone with `ok clone`, enable auto-sync, and resolve merge conflicts.
</Card>

<Card title="Team sharing" href="/team-sharing">
  Publish projects with `ok share publish` and manage config sharing with `ok config-sharing`.
</Card>

<Card title="CLI reference" href="/cli-reference">
  Full `ok` subcommand listing including `auth`, `clone`, `sync`, and `share`.
</Card>

<Card title="Troubleshooting" href="/troubleshooting">
  Diagnose server locks, repair skills, and recover from stale state with `ok clean`.
</Card>

</CardGroup>
