M0: compilable skeleton — Kigi 0.1.0 fork surgery
Hard fork of xai-org/grok-build (Apache-2.0) re-targeted as Kigi, an
unofficial Kimi Code CLI community build.
Rename & identity
- 72 xai-*/xai-grok-* crates -> kigi-* (explicit: xai-grok-pager-bin ->
kigi-bin [binary `kigi`], xai-grok-pager -> kigi-tui; rest mechanical);
ptyctl, ptyctl-cli, third_party/ unchanged; proto package
xai.grok.tools.v1 -> kigi.tools.v1
- Config home ~/.kigi (KIGI_SHARE_DIR override), env prefix GROK_* ->
KIGI_*, `kigi --version` carries the unofficial-community-build notice
- clap identity, help text, startup banner, prompt templates rebranded
(templates re-encrypted)
Deletions (PRD removal list #5/#6/#7/#9/#10)
- voice input (xai-grok-voice) and all TUI wiring
- telemetry: Mixpanel client, external OTel stream, Sentry, OTLP layers,
trace/GCS/S3 upload queues (kigi-file-utils halved), workspace upload
module & dc_log, heap-profile uploader, auth-diagnostics uploader,
session-analytics halves of feedback; local zero-egress observability
preserved in new kigi-log crate (unified log, --debug firehose,
subsystem file logs, opt-in instrumentation)
- announcements (crate, remote-settings fields, TUI surfaces)
- plugin marketplace (crate, sources/browse/CTA/extensions-modal tab);
direct plugin install/uninstall/update via kigi-agent git_install kept
- relay/gateway/assets endpoints and features (agent relay, headless
relay transport, gateway bridge, LeaderEnvUrls); leader IPC socket now
~/.kigi/leader.sock + KIGI_LEADER_SOCKET, no ws-url derivation
- functional types rehomed instead of deleted: PermissionMode ->
kigi-config-types, McpInitStrategy -> kigi-mcp, PrCreationSource ->
session signals, TerminalDiagnostics -> kigi-pager-render, agent_id ->
shell util
Endpoints
- kigi-env rewritten: single production KigiEndpoints {coding_api_base_url
https://api.kimi.com/coding/v1 (KIGI_CODE_BASE_URL), oauth_host
https://auth.kimi.com (KIGI_OAUTH_HOST), update_base_url (GitHub
Releases API), upgrade_page_url}; GrokBuildEnvironment enum deleted
Toolchain & workspace hygiene
- Rust 1.97.0 pinned; edition 2024; full cargo update; git2 hoisted to
workspace at 0.21 (Option->Result API migration), quick-xml 0.41
- Root Cargo.toml hand-maintained (PRD §8.1): version 0.1.0 inherited by
all members, members sorted, unused deps pruned
- cargo-deny advisories gate (deny.toml with documented transitive
exceptions); CI workflow (check/clippy/fmt/deny/test, macOS+Linux)
- cross-crate test seams re-gated behind `test-support` cargo feature;
insta snapshot baselines renamed to the kigi_tui prefix
- clippy --workspace --all-targets: zero warnings; fmt clean
Fixes surfaced by the port
- updater probe/installer divergence (bin/kigi vs bin/grok symlink set)
- idle model-metadata refresh dead under KIGI_CODE_BASE_URL override
(new is_effective_coding_endpoint_url, loopback+override aware)
- macOS symlinked-TMPDIR fixture canonicalization (foreign_sessions,
fast-worktree); RSS measurement tests serialized via serial_test
Docs & legal (Apache §4)
- NOTICE added (upstream attribution + change statement); THIRD-PARTY
notices sustained; kigi-tools ported-code notices extended; README,
CONTRIBUTING, SECURITY, AGENTS.md rewritten
Out of scope for M0 (tracked): Kimi auth/inference (M1), search/fetch,
command parity, config import (M2), Computer Hub excision & final
brand-token sweep (M2), distribution & self-update rewrite (M3).
This commit is contained in:
@@ -0,0 +1,300 @@
|
||||
# Authentication
|
||||
|
||||
Grok supports several authentication methods, including interactive browser login, enterprise single sign-on (SSO), and headless CI/CD runners.
|
||||
|
||||
---
|
||||
|
||||
## Browser Login (Default)
|
||||
|
||||
On first launch, Grok opens your browser to authenticate with grok.com:
|
||||
|
||||
```bash
|
||||
grok
|
||||
```
|
||||
|
||||
Grok stores credentials in `~/.kigi/auth.json` and reuses them across sessions. Grok refreshes access tokens automatically in the background. When a token can't be refreshed, Grok prompts you to sign in again. Credentials without a server-provided expiry fall back to a 30-day lifetime.
|
||||
|
||||
### Re-authenticate
|
||||
|
||||
To switch accounts or resolve an authentication problem, run:
|
||||
|
||||
```bash
|
||||
grok login
|
||||
```
|
||||
|
||||
Running `grok login` starts the sign-in flow again, replacing your cached session. By default, it opens your browser and signs in through SpaceXAI OAuth at `auth.x.ai`. Pass a flag to select a different flow:
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--oauth` | Sign in through SpaceXAI OAuth at `auth.x.ai`. This is the default, so the flag is optional. |
|
||||
| `--device-auth` (alias `--device-code`) | Sign in with the device-code flow for headless or remote environments. |
|
||||
|
||||
To sign out, run `grok logout`. It takes no flags and clears your cached credentials.
|
||||
|
||||
---
|
||||
|
||||
## API Key
|
||||
|
||||
For CI/CD, automation, or environments without browser access, use an API key from [console.x.ai](https://console.x.ai):
|
||||
|
||||
```bash
|
||||
export XAI_API_KEY="xai-..."
|
||||
grok
|
||||
```
|
||||
|
||||
Grok uses the API key as a fallback when no session token is active. If you have already signed in interactively, the stored session token takes precedence. To fall back to the API key, run `grok logout` or delete `~/.kigi/auth.json`.
|
||||
|
||||
---
|
||||
|
||||
## OIDC (Customer SSO)
|
||||
|
||||
Authenticate developers through your own Identity Provider (IdP) -- such as Okta, Azure AD, or Auth0 -- instead of grok.com.
|
||||
|
||||
### 1. Register a public client in your IdP
|
||||
|
||||
- Grant type: Authorization Code with PKCE (Proof Key for Code Exchange)
|
||||
- Redirect URI: `http://127.0.0.1/callback` -- a loopback address. Grok binds a random port at sign-in time, and most IdPs treat the loopback redirect as port-agnostic per [RFC 8252](https://tools.ietf.org/html/rfc8252).
|
||||
- No client secret. PKCE replaces it.
|
||||
|
||||
### 2. Configure the CLI
|
||||
|
||||
Via config file:
|
||||
|
||||
```toml
|
||||
# ~/.kigi/config.toml
|
||||
[grok_com_config.oidc]
|
||||
issuer = "https://acme.okta.com"
|
||||
client_id = "0oa1b2c3d4e5f6g7h8i9"
|
||||
```
|
||||
|
||||
Or via environment variables:
|
||||
|
||||
```bash
|
||||
export KIGI_OIDC_ISSUER="https://acme.okta.com"
|
||||
export KIGI_OIDC_CLIENT_ID="0oa1b2c3d4e5f6g7h8i9"
|
||||
```
|
||||
|
||||
You can also override the API endpoint to point at your own proxy:
|
||||
|
||||
```bash
|
||||
export KIGI_CLI_CHAT_PROXY_BASE_URL="https://grok-proxy.acme.com/v1"
|
||||
```
|
||||
|
||||
### 3. Run `grok`
|
||||
|
||||
The CLI discovers endpoints via `{issuer}/.well-known/openid-configuration`, opens the IdP login page, and stores tokens in `~/.kigi/auth.json`. Tokens auto-refresh silently via the stored `refresh_token`.
|
||||
|
||||
### Optional fields
|
||||
|
||||
| Field | Default | Notes |
|
||||
|-------|---------|-------|
|
||||
| `scopes` | `["openid", "profile", "email", "offline_access", "api:access"]` | `offline_access` enables silent token refresh |
|
||||
| `audience` | None | Required by some IdPs (e.g., Auth0) |
|
||||
|
||||
---
|
||||
|
||||
## External Auth Provider
|
||||
|
||||
When browser-based login isn't possible -- for example, on sandboxed VMs, CI runners, or air-gapped networks -- delegate authentication to an external binary or script.
|
||||
|
||||
### How It Works
|
||||
|
||||
```
|
||||
+--------------+ sh -c +------------------------+
|
||||
| Grok |-------------->| your auth binary |
|
||||
| | | |
|
||||
| reads |<-- stdout ----| prints token |
|
||||
| auth.json | | |
|
||||
| | (stderr) | prints status/URLs |--> surfaced to user
|
||||
+--------------+ +------------------------+
|
||||
```
|
||||
|
||||
1. Grok runs your command via `sh -c "<command>"`
|
||||
2. Your binary runs whatever auth flow it needs (SSO, device code, certificate exchange)
|
||||
3. **stderr** carries human-readable output, such as login URLs and status messages. Grok reads stderr and surfaces it to the user; in the TUI, it turns the first `https://` URL into a clickable sign-in link.
|
||||
4. **stdout** is captured by Grok and saved as the access token
|
||||
5. Exit 0 = success; exit non-zero = Grok falls back to interactive login
|
||||
|
||||
### The stdout / stderr Contract
|
||||
|
||||
| Stream | What to print | Who sees it |
|
||||
|--------|---------------|-------------|
|
||||
| **stdout** | The token -- nothing else | Grok (parsed and stored in auth.json) |
|
||||
| **stderr** | Login URLs, status messages, errors | The user (Grok reads stderr and shows the sign-in URL as a clickable link in the TUI) |
|
||||
|
||||
**Do not print anything to stdout except the token.** No progress messages, no debug output. Grok reads stdout, trims surrounding whitespace, and parses the result as a token.
|
||||
|
||||
### stdout Token Format
|
||||
|
||||
**Bare string** -- just the raw token:
|
||||
|
||||
```
|
||||
eyJhbGciOiJSUzI1NiIs...
|
||||
```
|
||||
|
||||
**JSON** -- with optional refresh token, expiry, and issuer:
|
||||
|
||||
```json
|
||||
{"access_token": "eyJhbGciOi...", "refresh_token": "ref-tok", "expires_in": 3600, "issuer": "https://idp.example.com"}
|
||||
```
|
||||
|
||||
Use JSON if your tokens expire and you want Grok to automatically re-run the binary before expiry.
|
||||
|
||||
JSON fields:
|
||||
|
||||
| Field | Required | Meaning |
|
||||
|-------|----------|---------|
|
||||
| `access_token` | yes | Bearer token Grok sends to the xAI API |
|
||||
| `refresh_token` | no | Stored for reference. Grok refreshes by re-running your binary, not with an OAuth refresh grant |
|
||||
| `expires_in` | no | Token lifetime in seconds; enables proactive refresh before expiry |
|
||||
| `issuer` | no | Identifies the token's issuer |
|
||||
|
||||
### Configuration
|
||||
|
||||
Via config file:
|
||||
|
||||
```toml
|
||||
# ~/.kigi/config.toml
|
||||
[auth]
|
||||
auth_provider_command = "/usr/local/bin/my-auth-provider"
|
||||
auth_provider_label = "Acme Corp" # optional -- customizes the TUI login button
|
||||
auth_token_ttl = 3600 # optional -- token lifetime in seconds
|
||||
```
|
||||
|
||||
Or via environment variables:
|
||||
|
||||
```bash
|
||||
export KIGI_AUTH_PROVIDER_COMMAND="/usr/local/bin/my-auth-provider"
|
||||
export KIGI_AUTH_PROVIDER_LABEL="Acme Corp"
|
||||
export KIGI_AUTH_TOKEN_TTL=3600
|
||||
```
|
||||
|
||||
### Token Refresh
|
||||
|
||||
When Grok needs to refresh an expired token, it re-runs your binary with `KIGI_AUTH_EXPIRED=1` set in the environment. Each run fully replaces the stored credential, so emit the same JSON fields (such as `issuer`) on every invocation, including refreshes. Your binary can use this to take a faster silent-refresh path:
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
if [ "$KIGI_AUTH_EXPIRED" = "1" ]; then
|
||||
echo "Refreshing token..." >&2
|
||||
TOKEN=$(my-company-auth --refresh --silent)
|
||||
else
|
||||
echo "Authenticating via Acme Corp SSO..." >&2
|
||||
TOKEN=$(my-company-auth --login --interactive)
|
||||
fi
|
||||
|
||||
if [ -z "$TOKEN" ]; then
|
||||
echo "Authentication failed" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "{\"access_token\": \"$TOKEN\", \"expires_in\": 3600}"
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `KIGI_AUTH_PROVIDER_COMMAND` | Path to your auth binary |
|
||||
| `KIGI_AUTH_PROVIDER_LABEL` | Display name on the TUI login screen (e.g., "Acme Corp") |
|
||||
| `KIGI_AUTH_TOKEN_TTL` | Token lifetime in seconds (for bare-string tokens without `expires_in`) |
|
||||
| `KIGI_AUTH_EXPIRED` | Set to `1` by Grok when re-running the binary for token refresh |
|
||||
| `KIGI_AUTH_EARLY_INVALIDATION_SECS` | Seconds before expiry to proactively refresh (default: 300) |
|
||||
|
||||
---
|
||||
|
||||
## Device Code Flow
|
||||
|
||||
For headless environments (SSH sessions, Docker containers, remote VMs) where no browser is available locally:
|
||||
|
||||
```bash
|
||||
grok login --device-auth # or: grok login --device-code
|
||||
```
|
||||
|
||||
This prints a URL and code to the terminal. Open the URL on any device, enter the code, and complete authentication. Grok polls until the login is confirmed.
|
||||
|
||||
You can also implement the device-code flow through an [External Auth Provider](#external-auth-provider) for full control.
|
||||
|
||||
---
|
||||
|
||||
## Automatic Credential Refresh
|
||||
|
||||
Grok automatically refreshes expired credentials:
|
||||
|
||||
- **Before expiry:** If your auth provider returned `expires_in` (JSON output) or you set `auth_token_ttl`, Grok re-runs the auth binary ~5 minutes before expiry.
|
||||
- **On auth error:** If the server returns 401 Unauthorized, Grok refreshes the credentials and retries the request.
|
||||
- **OIDC:** If a `refresh_token` is available, Grok silently refreshes via your IdP without re-opening the browser.
|
||||
|
||||
Tune the refresh buffer:
|
||||
|
||||
```bash
|
||||
# Refresh 5 minutes before expiry (default)
|
||||
export KIGI_AUTH_EARLY_INVALIDATION_SECS=300
|
||||
|
||||
# Disable the proactive buffer: refresh at expiry or on a 401 (set to 0)
|
||||
export KIGI_AUTH_EARLY_INVALIDATION_SECS=0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hot Reload
|
||||
|
||||
Grok picks up changes to `~/.kigi/auth.json` automatically. If you update credentials externally (for example, with a script that writes new tokens), Grok uses the new credentials on the next API call without a restart.
|
||||
|
||||
---
|
||||
|
||||
## Auth Precedence
|
||||
|
||||
Grok resolves credentials for each request in this order, highest to lowest:
|
||||
|
||||
1. **Per-model `api_key` or `env_key`** -- set under `[model.<name>]` in `config.toml`. Wins whenever present.
|
||||
2. **Active session token** -- obtained through browser, OIDC/OAuth2, or external-provider login and stored in `~/.kigi/auth.json`.
|
||||
3. **`XAI_API_KEY`** -- fallback when no session token is active.
|
||||
|
||||
When more than one login flow is configured, Grok populates the session token from the first available source, highest to lowest:
|
||||
|
||||
1. **External auth provider** (`auth_provider_command`)
|
||||
2. **Enterprise OIDC** -- when OIDC is configured, through `[grok_com_config.oidc]` in `config.toml` or the `KIGI_OIDC_ISSUER` and `KIGI_OIDC_CLIENT_ID` environment variables
|
||||
3. **SpaceXAI OAuth2 browser login** -- the default
|
||||
|
||||
During a session, the active method handles all mid-session refreshes.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Debug logging
|
||||
|
||||
Set `RUST_LOG` to control the verbosity of the file log and headless stderr output. (The TUI's on-screen tracing pane uses a fixed filter and ignores `RUST_LOG`.) In the TUI, file logging defaults to `DEBUG`; in headless mode (`-p`), `RUST_LOG` defaults to `off` so only the answer is printed — set `RUST_LOG=error` (or broader) to see logs on stderr.
|
||||
|
||||
In the TUI, set `KIGI_LOG_FILE` to an absolute path to write logs to that file:
|
||||
|
||||
```bash
|
||||
KIGI_LOG_FILE=/tmp/grok.log RUST_LOG=debug grok
|
||||
tail -f /tmp/grok.log
|
||||
```
|
||||
|
||||
`KIGI_LOG_FILE` is treated as a literal file path. A relative value such as `1` writes a file named `1` in the current directory.
|
||||
|
||||
In headless mode, logs go to stderr. Redirect them to a file:
|
||||
|
||||
```bash
|
||||
RUST_LOG=debug grok -p "hello" 2> /tmp/grok.log
|
||||
```
|
||||
|
||||
### Common log messages
|
||||
|
||||
| Log message | What it means |
|
||||
|-------------|---------------|
|
||||
| `auth: running external auth provider` | Grok is running your binary |
|
||||
| `auth: external auth provider returned fresh token` | Grok parsed and stored the token |
|
||||
| `auth: external auth provider failed` | Binary exited non-zero or stdout was empty |
|
||||
| `auth: external auth provider timed out (likely needs interactive auth), killing` | Binary did not exit before the timeout and was killed |
|
||||
| `auth: failed to start external auth provider` | Command could not be spawned (binary not found) |
|
||||
|
||||
### Common fixes
|
||||
|
||||
- **"Authentication failed"** -- Run `grok logout` to clear cached credentials, then `grok login` to sign in again.
|
||||
- **Token expires too quickly** -- Set `auth_token_ttl` or return `expires_in` in your auth provider's JSON output.
|
||||
- **OIDC redirect fails** -- Ensure your IdP allows loopback redirect URIs (`http://127.0.0.1/callback`).
|
||||
- **External auth provider not found** -- Check that the `auth_provider_command` path is correct and the binary is executable.
|
||||
Reference in New Issue
Block a user