Skip to content

feat: browser-based credential entry from CLI — secrets via UI instead of terminal #242

@ctkm-aelf

Description

@ctkm-aelf

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:

  1. Binds ephemeral local server on 127.0.0.1:0 (random port)
  2. Generates random state parameter (16 bytes hex)
  3. Opens browser: {frontend_url}/cli-auth?port={PORT}&state={STATE}
  4. Waits for callback on /callback?...&state=... (120s timeout)
  5. 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

  1. Call POST /api/v1/keys with form data
  2. On success → redirect to http://localhost:{port}/callback?state={state}&status=ok&service_id={id}
  3. 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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions