Skip to content

credential: manage keystore credentials from the CLI #1218

Description

@lukehinds

Credential injection (nono run --credential <service> -- ...) reads secrets from the system keystore, but nono gives users no way to put secrets there. To set this up, a user currently has to use platform-specific tooling and figuring out the nono json policy schema:

  • macOS: security add-generic-password -s nono -a openai -w …
  • Linux: secret-tool store --label=nono service nono account openai

This issue is for more new users, then enterprise - who want to get going, yet could easily get things wrong. The most common failure is a service/account mismatch: the account you write with security/secret-tool has to be exactly the account the proxy reads for that service at run time, and nothing tells you what that account is. A built-in service might read from an env var or an external manager (1Password, Bitwarden) ather than the keystore at all - so a hand-written keystore entry is silently ignored, with no feedback about why injection "isn't working."

There's also no way to inspect the situation: which services exist, where each one's credential comes from, and whether it's actually available.

Matters would be helped with a nono credential command for storing and inspecting keystore credentials directly, so setup is:

nono credential set openai            # paste the key once (no echo)
nono run --credential openai -- claude

Subcommands:

  • set <service> — prompt securely (terminal echo disabled) and store the
    secret under the account the proxy actually reads for that service. Falls back
    to plain line-reading when stdin isn't a TTY, so it stays scriptable.
  • list — show built-in and (with --profile) profile custom services,
    their source (keystore / env / external manager / oauth2), and
    availability. --json supported.
  • rm <service> — remove a stored credential (with a confirmation prompt;
    -y to skip).
  • check <service> — verify a service's credential loads, without ever
    printing the value (exits non-zero if it can't).

Resolution: service names resolve against the same built-in network policy and profile custom credentials that --credential <service> uses, so the account written by set is the account injected at run time. Non-keystore sources are handled explicitly:

  • An env-backed service (e.g. anthropicenv://ANTHROPIC_API_KEY) declines set with guidance to export the variable, rather than writing an unused keystore entry. `--account forces a keystore entry when needed.
  • External-manager-backed services (1Password/Bitwarden/Apple Passwords/file) are surfaced with a redacted display and similarly declined.
  • OAuth2 client-credential services point you at storing each half under its own account.

Library/CLI boundary: the nono library gains the keystore primitives
(store_secret / delete_secret / secret_exists, gated on the system-keyring feature, alongside the existing load_secret_by_ref); all policy and UX live in the CLI.

Example list output:

SERVICE    ORIGIN    SOURCE                  STATUS
anthropic  built-in  env: ANTHROPIC_API_KEY  ✗ not set
gemini     built-in  keystore: gemini        ✓ available
github     built-in  env: GITHUB_TOKEN       ✓ available
openai     built-in  keystore: openai        ✗ not set

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions