feat(keys): first-class BYOK key management — store, catalog, /api/keys
The auth/keys wave (2026-09-19): the server now owns the flat auth.json map directly (LH_AUTH_FILE from main.cjs; repo-root auth.json in dev, gitignored). The store reflects itself into the child's env at boot with fill-gap precedence — real env (incl. .env.local in dev) always wins — so every consumer keeps its plain process.env reads and saved keys take effect immediately, no restart; main.cjs's spawn-time injection retired (an env copy would shadow every settings-page change). Curated 4-key catalog (OpenRouter, Deepgram, Google search pair) with shape-only validation (no live pings — owner decision), 400s on bad pastes, last-4+length masking (plaintext never leaves the store), unknown file vars preserved + surfaced. Routes: GET /api/keys, PUT/DELETE /api/keys/:id. Tests: catalog/store/composition, with the env-sanitizing prologue (a dev shell may export real keys; tests use throwaway values only).
This commit is contained in:
@@ -182,3 +182,21 @@ export const SETTINGS_FILE = path.join(DATA_ROOT, "settings.json");
|
||||
* a vanished segment stops composing but stays on disk.
|
||||
*/
|
||||
export const PROMPTS_OVERRIDE_DIR = path.join(DATA_ROOT, "prompts");
|
||||
|
||||
/**
|
||||
* THE AUTH/KEY STORE (auth-keys wave, 2026-09-19): BYOK provider keys in a
|
||||
* flat {VAR: value} JSON map — the SAME file the Electron app has always
|
||||
* hand-edited (owner decision: option A, zero migration). Desktop main.cjs
|
||||
* passes LH_AUTH_FILE pointing at userData/auth.json (mode 0600); in dev
|
||||
* (no Electron) the default is the repo-root auth.json (gitignored, same
|
||||
* pattern as .env.local — a worktree may symlink it to the master copy).
|
||||
* The SERVER reads/writes this file directly at call time (no restart to
|
||||
* change a key); it reflects the values into process.env at boot with
|
||||
* FILL-GAP precedence — real environment variables (incl. .env.local in
|
||||
* dev) always win (src/server/keys.ts). Hand-edits still work: the file is
|
||||
* re-read from disk on every API access. Secrets NEVER go in settings.json
|
||||
* — this file keeps that role, alongside .env.local.
|
||||
*/
|
||||
export const AUTH_FILE = process.env.LH_AUTH_FILE
|
||||
? path.resolve(process.env.LH_AUTH_FILE)
|
||||
: path.join(ROOT, "auth.json");
|
||||
|
||||
Reference in New Issue
Block a user