From 20e6e08a42240aef0b36ecf627cbcde921912071 Mon Sep 17 00:00:00 2001 From: t Date: Sat, 13 Jun 2026 14:24:31 -0600 Subject: auth: flatten [auth.] config + ${...} substitution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Collapse the auth schema: infer `type` from keys, replace key/key_env_var with a single `key` (resolved via substitution), flatten the exchange to flat `exchange_*` keys, and drop the auth-level headers tables in favor of reusing the provider's `extra_headers` on the auth HTTP calls. Add `${sibling}` and `${env:VAR}` substitution over auth values (GitHub Enterprise = set `domain`). Restore api_key-empty → provider-drop. Update default config, docs, and tests. --- docs/oauth-provider-auth.md | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'docs/oauth-provider-auth.md') diff --git a/docs/oauth-provider-auth.md b/docs/oauth-provider-auth.md index 9f5c7fe..96312e1 100644 --- a/docs/oauth-provider-auth.md +++ b/docs/oauth-provider-auth.md @@ -501,10 +501,28 @@ None of these are required for Copilot or Codex auth correctness. The first pass landed as a clean break (no compatibility shim): every networked provider must name an `[auth.]` session via `auth = ""`; provider-level `api_key`/`api_key_env_var` are gone. Resolved decisions for the -open questions above: TOML uses `key_env_var`; `auth` is required for networked -providers; Codex device auth is modelled as a `dialect = "codex"` variant of -the generic `oauth_device` type; keychain storage is deferred (file-backed -`$PANTO_HOME/auth/.json`, owner-only `0600`). +open questions above: `auth` is required for networked providers; Codex device +auth is a `dialect = "codex"` variant of the generic `oauth_device` type; +keychain storage is deferred (file-backed `$PANTO_HOME/auth/.json`, +owner-only `0600`). + +The `[auth.]` schema is deliberately flat and minimal: + +- **`type` is inferred** — `key` ⇒ `api_key`, `client_id` ⇒ `oauth_device` + (explicit `type` still works). +- **`${...}` substitution** on every value — `${env:VAR}` reads the + environment, `${sibling}` reads another key in the same section. The common + api-key session is one line: `key = "${env:OPENAI_API_KEY}"`. An unset + `${env:VAR}` resolves to empty, and a provider whose `api_key` resolves empty + is **dropped** (reproducing "export your key or the provider disappears"). + GitHub Enterprise is a one-liner: set `domain = "company.ghe.com"` and the + `${domain}`-templated URLs follow. +- **The exchange is flat** — `exchange_url`, `exchange_method`, + `exchange_token_path`, `exchange_expires_path`, `exchange_base_url_path` (no + `[auth..exchange]` sub-table); presence of `exchange_url` enables it. +- **No auth-level headers** — the provider's `extra_headers` (the client + identity, e.g. Copilot's editor headers) are reused on the auth HTTP calls + (device/poll/exchange) as well as the model request, so they're written once. ### What core owns (libpanto) @@ -519,8 +537,9 @@ the generic `oauth_device` type; keychain storage is deferred (file-backed ### What the CLI owns (`src/`) - `config_file.zig` parses `[auth.]` and the `auth = ""` reference, - resolving `api_key` sessions eagerly (literal/env). Providers always survive - resolution; an unresolved session fails the request with a clear error. + runs `${...}`/`${env:VAR}` substitution, infers `type`, and resolves + `api_key` sessions eagerly. A provider whose `api_key` resolves empty is + dropped; OAuth providers always survive (resolved at turn time). - `auth_manager.zig` resolves the active provider's auth before each turn (load → login → refresh → exchange → build credential), patching the live `ProviderConfig` (bearer, dynamic `base_url`, merged headers). -- cgit v1.3