Problem
When users add a service via CLI (nyxid service add), they enter API keys/secrets directly in the terminal. This means secrets can appear in terminal scrollback and shell history. For a security-focused product, credentials should be entered in the browser instead.
Proposed flow
Same pattern as nyxid login — CLI starts a local HTTP server, opens browser, browser handles sensitive input:
Terminal Browser
──────── ───────
$ nyxid service add llm-openai
Opening browser for credential
entry...
Waiting...
→ /cli/add-credential?port=PORT&state=STATE&slug=llm-openai
Pre-filled: service name, auth method, endpoint
User pastes API key into form
User clicks "Save"
← Redirects to localhost:PORT/callback?status=ok
✅ Service "llm-openai" added
Proxy: /api/v1/proxy/s/llm-openai
No new backend endpoints needed. Frontend submits to existing POST /api/v1/keys. CLI picks up the result via localhost callback (same as nyxid login).
CLI reference — nyxid login pattern to follow
The existing nyxid login flow:
- Binds ephemeral local server on
127.0.0.1:0 (random port)
- Generates random
state parameter (16 bytes hex)
- Opens browser:
{frontend_url}/cli-auth?port={PORT}&state={STATE}
- Waits for callback on
/callback?...&state=... (120s timeout)
- Validates state, extracts result
The credential entry flow should follow this exact pattern.
CLI reference — nyxid service add current flags
The frontend page needs to support the same operations the CLI currently handles:
nyxid service add [SLUG] [OPTIONS]
Arguments:
SLUG Catalog service slug (e.g. llm-openai). Omit with --custom.
Options:
--custom Custom endpoint (no catalog)
--oauth Use OAuth flow
--device-code Use device code flow
--via-node <NODE_ID> Route through a node
--endpoint-url <URL> Override endpoint URL
--label <LABEL> Custom label
--auth-method <METHOD> bearer | bot_bearer | header | query | path | basic | body | none | token_exchange
--auth-key-name <NAME> Key name (e.g. Authorization, X-API-Key)
--credential <VALUE> Credential value (this is what moves to browser)
--credential-env <VAR> Read from env var
--scope <SCOPES> OAuth scopes (with --oauth/--device-code)
API endpoints the frontend page calls
All existing — no new backend work:
| Action |
Endpoint |
Notes |
| Fetch catalog |
GET /api/v1/catalog |
List available services |
| Fetch catalog entry |
GET /api/v1/catalog/{slug} |
Pre-fill defaults (auth method, endpoint, key name) |
| Create service + credential |
POST /api/v1/keys |
Existing unified key API |
| OAuth flow |
GET /api/v1/providers/{id}/connect/oauth |
If service uses OAuth |
| Device code flow |
POST /api/v1/providers/{id}/connect/device-code/initiate |
If service uses device code |
Frontend page spec — /cli/add-credential
URL params
port — CLI local server port (for callback redirect)
state — CSRF state token (validate on callback)
slug — (optional) catalog service slug, pre-fills the form
custom — (optional) flag for custom endpoint mode
Form fields
Catalog mode (slug provided):
- Service name + endpoint pre-filled from catalog
- Auth method pre-filled (user can override)
- Credential input — single password field for the secret
- Node routing — optional, select from user's nodes
Custom mode (--custom):
- Label (text input)
- Endpoint URL (text input)
- Auth method (dropdown: bearer, header, query, basic, body, none)
- Auth key name (text input, default based on auth method)
- Credential input — password field
OAuth/device code mode:
- Trigger OAuth redirect or show device code — same as existing add-key dialog
On submit
- Call
POST /api/v1/keys with form data
- On success → redirect to
http://localhost:{port}/callback?state={state}&status=ok&service_id={id}
- On error → show error inline, do not redirect
Auth methods supported (match CLI)
| Method |
auth_key_name default |
Credential field label |
bearer |
Authorization |
"Bearer Token" |
bot_bearer |
Authorization |
"Bot Token" |
header |
X-API-Key |
"API Key" |
query |
key |
"API Key" |
path |
bot |
"Path Token" |
basic |
— |
"Username:Password" |
body |
app_secret |
"App Secret" |
none |
— |
(no credential field) |
token_exchange |
(catalog-defined) |
(multi-field, catalog-defined) |
CLI changes
nyxid service add — default: open browser (same as login)
--no-browser flag — fallback to current interactive terminal prompt (for CI/headless)
- Pass
slug, custom, and any pre-set flags via URL params so the form is pre-filled
References
Problem
When users add a service via CLI (
nyxid service add), they enter API keys/secrets directly in the terminal. This means secrets can appear in terminal scrollback and shell history. For a security-focused product, credentials should be entered in the browser instead.Proposed flow
Same pattern as
nyxid login— CLI starts a local HTTP server, opens browser, browser handles sensitive input:No new backend endpoints needed. Frontend submits to existing
POST /api/v1/keys. CLI picks up the result via localhost callback (same asnyxid login).CLI reference —
nyxid loginpattern to followThe existing
nyxid loginflow:127.0.0.1:0(random port)stateparameter (16 bytes hex){frontend_url}/cli-auth?port={PORT}&state={STATE}/callback?...&state=...(120s timeout)The credential entry flow should follow this exact pattern.
CLI reference —
nyxid service addcurrent flagsThe frontend page needs to support the same operations the CLI currently handles:
API endpoints the frontend page calls
All existing — no new backend work:
GET /api/v1/catalogGET /api/v1/catalog/{slug}POST /api/v1/keysGET /api/v1/providers/{id}/connect/oauthPOST /api/v1/providers/{id}/connect/device-code/initiateFrontend page spec —
/cli/add-credentialURL params
port— CLI local server port (for callback redirect)state— CSRF state token (validate on callback)slug— (optional) catalog service slug, pre-fills the formcustom— (optional) flag for custom endpoint modeForm fields
Catalog mode (slug provided):
Custom mode (--custom):
OAuth/device code mode:
On submit
POST /api/v1/keyswith form datahttp://localhost:{port}/callback?state={state}&status=ok&service_id={id}Auth methods supported (match CLI)
auth_key_namedefaultbearerAuthorizationbot_bearerAuthorizationheaderX-API-Keyquerykeypathbotbasicbodyapp_secretnonetoken_exchangeCLI changes
nyxid service add— default: open browser (same as login)--no-browserflag — fallback to current interactive terminal prompt (for CI/headless)slug,custom, and any pre-set flags via URL params so the form is pre-filledReferences
nyxid login— existing CLI→browser pattern (cli/src/commands/login.rs)POST /api/v1/keys— existing unified key APIGET /api/v1/catalog— existing catalog API